diff --git a/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java b/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java index 5c3a8b28..6082d562 100644 --- a/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java +++ b/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java @@ -759,7 +759,7 @@ public abstract class AbstractBlockChain { 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 diff --git a/core/src/main/java/org/bitcoinj/core/AddressMessage.java b/core/src/main/java/org/bitcoinj/core/AddressMessage.java index 0a2e74cd..380e980c 100644 --- a/core/src/main/java/org/bitcoinj/core/AddressMessage.java +++ b/core/src/main/java/org/bitcoinj/core/AddressMessage.java @@ -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 @@ -67,7 +64,7 @@ public class AddressMessage extends Message { throw new ProtocolException("Address message too large."); addresses = new ArrayList((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(); } diff --git a/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java b/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java index 11ba1523..19fb58fb 100644 --- a/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java +++ b/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java @@ -41,15 +41,15 @@ import static org.bitcoinj.core.Utils.*; *
  • Message.bitcoinSerializeToStream() needs to be properly subclassed
  • * */ -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, String> names = new HashMap, String>(); + private static final Map, String> names = new HashMap, String>(); static { names.put(VersionMessage.class, "version"); @@ -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; } /** @@ -99,6 +106,7 @@ public class BitcoinSerializer { /** * 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); @@ -123,6 +131,7 @@ public class BitcoinSerializer { /** * 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) { @@ -134,6 +143,7 @@ public class BitcoinSerializer { /** * 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. // @@ -159,6 +169,7 @@ public class BitcoinSerializer { * 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); } @@ -167,6 +178,7 @@ public class BitcoinSerializer { * 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); @@ -198,25 +210,22 @@ public class BitcoinSerializer { 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")) { @@ -226,9 +235,9 @@ public class BitcoinSerializer { } 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")) { @@ -246,6 +255,100 @@ public class BitcoinSerializer { 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) { @@ -270,6 +373,7 @@ public class BitcoinSerializer { /** * Whether the serializer will produce lazy parse mode Messages */ + @Override public boolean isParseLazyMode() { return parseLazy; } @@ -277,6 +381,7 @@ public class BitcoinSerializer { /** * Whether the serializer will produce cached mode Messages */ + @Override public boolean isParseRetainMode() { return parseRetain; } diff --git a/core/src/main/java/org/bitcoinj/core/Block.java b/core/src/main/java/org/bitcoinj/core/Block.java index aeb394cf..e0cff3ee 100644 --- a/core/src/main/java/org/bitcoinj/core/Block.java +++ b/core/src/main/java/org/bitcoinj/core/Block.java @@ -87,16 +87,16 @@ public class Block extends Message { /** Stores the hash of the block. If null, getHash() will recalculate it. */ private Sha256Hash hash; - private boolean headerParsed; - private boolean transactionsParsed; + protected boolean headerParsed; + protected boolean transactionsParsed; - private boolean headerBytesValid; - private boolean transactionBytesValid; + protected boolean headerBytesValid; + protected boolean transactionBytesValid; // Blocks can be encoded in a way that will use more bytes than is optimal (due to VarInts having multiple encodings) // MAX_BLOCK_SIZE must be compared to the optimal encoding, not the actual encoding, so when parsing, we keep track // of the size of the ideal encoding in addition to the actual message size (which Message needs) - private int optimalEncodingMessageSize; + protected int optimalEncodingMessageSize; /** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */ Block(NetworkParameters params) { @@ -110,27 +110,46 @@ public class Block extends Message { length = 80; } - /** Constructs a block object from the Bitcoin wire format. */ + /** + * Constructs a block object from the Bitcoin wire format. + * @deprecated Use {@link BitcoinSerializer#makeBlock(byte[])} instead. + */ + @Deprecated public Block(NetworkParameters params, byte[] payloadBytes) throws ProtocolException { - super(params, payloadBytes, 0, false, false, payloadBytes.length); + super(params, payloadBytes, 0, params.getDefaultSerializer(), payloadBytes.length); } /** * Contruct a block object from the Bitcoin wire format. * @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 message. * @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 */ - public Block(NetworkParameters params, byte[] payloadBytes, boolean parseLazy, boolean parseRetain, int length) + public Block(NetworkParameters params, byte[] payloadBytes, MessageSerializer serializer, int length) throws ProtocolException { - super(params, payloadBytes, 0, parseLazy, parseRetain, length); + super(params, payloadBytes, 0, serializer, length); } + /** + * Construct a block object from the Bitcoin wire format. Used in the case of a block + * contained within another message (i.e. for AuxPoW header). + * + * @param params NetworkParameters object. + * @param payloadBytes Bitcoin protocol formatted byte array containing message content. + * @param offset The location of the first payload byte within the array. + * @param parent The message element which contains this block, maybe null for no parent. + * @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 + */ + public Block(NetworkParameters params, byte[] payloadBytes, int offset, @Nullable Message parent, MessageSerializer serializer, int length) + throws ProtocolException { + // TODO: Keep the parent + super(params, payloadBytes, offset, serializer, length); + } /** * Construct a block initialized with all the given fields. @@ -185,14 +204,25 @@ public class Block extends Message { hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(payload, offset, cursor)); headerParsed = true; - headerBytesValid = parseRetain; + headerBytesValid = serializer.isParseRetainMode(); } protected void parseTransactions() throws ProtocolException { + parseTransactions(offset + HEADER_SIZE); + } + + /** + * Parse transactions from the block. + * + * @param transactionsOffset Offset of the transactions within the block. + * Useful for non-Bitcoin chains where the block header may not be a fixed + * size. + */ + protected void parseTransactions(final int transactionsOffset) throws ProtocolException { if (transactionsParsed) return; - cursor = offset + HEADER_SIZE; + cursor = transactionsOffset; optimalEncodingMessageSize = HEADER_SIZE; if (payload.length == cursor) { // This message is just a header, it has no transactions. @@ -205,7 +235,7 @@ public class Block extends Message { optimalEncodingMessageSize += VarInt.sizeOf(numTransactions); transactions = new ArrayList(numTransactions); for (int i = 0; i < numTransactions; i++) { - Transaction tx = new Transaction(params, payload, cursor, this, parseLazy, parseRetain, UNKNOWN_LENGTH); + Transaction tx = new Transaction(params, payload, cursor, this, serializer, UNKNOWN_LENGTH); // Label the transaction as coming from the P2P network, so code that cares where we first saw it knows. tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK); transactions.add(tx); @@ -215,7 +245,7 @@ public class Block extends Message { // No need to set length here. If length was not provided then it should be set at the end of parseLight(). // If this is a genuine lazy parse then length must have been provided to the constructor. transactionsParsed = true; - transactionBytesValid = parseRetain; + transactionBytesValid = serializer.isParseRetainMode(); } @Override @@ -240,15 +270,15 @@ public class Block extends Message { // Ignore the header since it has fixed length. If length is not provided we will have to // invoke a light parse of transactions to calculate the length. if (length == UNKNOWN_LENGTH) { - Preconditions.checkState(parseLazy, + Preconditions.checkState(serializer.isParseLazyMode(), "Performing lite parse of block transaction as block was initialised from byte array " + "without providing length. This should never need to happen."); parseTransactions(); length = cursor - offset; } else { - transactionBytesValid = !transactionsParsed || parseRetain && length > HEADER_SIZE; + transactionBytesValid = !transactionsParsed || serializer.isParseRetainMode() && length > HEADER_SIZE; } - headerBytesValid = !headerParsed || parseRetain && length >= HEADER_SIZE; + headerBytesValid = !headerParsed || serializer.isParseRetainMode() && length >= HEADER_SIZE; } /* @@ -282,7 +312,7 @@ public class Block extends Message { return; try { parseTransactions(); - if (!parseRetain) { + if (!serializer.isParseRetainMode()) { transactionBytesValid = false; if (headerParsed) payload = null; diff --git a/core/src/main/java/org/bitcoinj/core/ChildMessage.java b/core/src/main/java/org/bitcoinj/core/ChildMessage.java index 3f80599e..c693e360 100644 --- a/core/src/main/java/org/bitcoinj/core/ChildMessage.java +++ b/core/src/main/java/org/bitcoinj/core/ChildMessage.java @@ -25,6 +25,10 @@ public abstract class ChildMessage extends Message { @Nullable protected Message parent; + /** + * @deprecated Use {@link #ChildMessage(NetworkParameters) instead. + */ + @Deprecated protected ChildMessage() { } @@ -36,9 +40,8 @@ public abstract class ChildMessage extends Message { super(params, payload, offset, protocolVersion); } - public ChildMessage(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, boolean parseLazy, - boolean parseRetain, int length) throws ProtocolException { - super(params, payload, offset, protocolVersion, parseLazy, parseRetain, length); + public ChildMessage(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, MessageSerializer setSerializer, int length) throws ProtocolException { + super(params, payload, offset, protocolVersion, setSerializer, length); this.parent = parent; } @@ -46,9 +49,9 @@ public abstract class ChildMessage extends Message { super(params, payload, offset); } - public ChildMessage(NetworkParameters params, byte[] payload, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) + public ChildMessage(NetworkParameters params, byte[] payload, int offset, @Nullable Message parent, MessageSerializer setSerializer, int length) throws ProtocolException { - super(params, payload, offset, parseLazy, parseRetain, length); + super(params, payload, offset, setSerializer, length); this.parent = parent; } diff --git a/core/src/main/java/org/bitcoinj/core/DummySerializer.java b/core/src/main/java/org/bitcoinj/core/DummySerializer.java new file mode 100644 index 00000000..efef9959 --- /dev/null +++ b/core/src/main/java/org/bitcoinj/core/DummySerializer.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Ross Nicoll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.bitcoinj.core; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +/** + * Dummy serializer used ONLY for objects which do not have network parameters + * set. + */ +class DummySerializer implements MessageSerializer { + public static final DummySerializer DEFAULT = new DummySerializer(); + + private static final String DEFAULT_EXCEPTION_MESSAGE = "Dummy serializer cannot serialize/deserialize objects as it does not know which network they belong to."; + + public DummySerializer() { + } + + @Override + public Message deserialize(ByteBuffer in) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public BitcoinSerializer.BitcoinPacketHeader deserializeHeader(ByteBuffer in) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Message deserializePayload(BitcoinSerializer.BitcoinPacketHeader header, ByteBuffer in) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public boolean isParseLazyMode() { + return false; + } + + @Override + public boolean isParseRetainMode() { + return false; + } + + @Override + public AddressMessage makeAddressMessage(byte[] payloadBytes, int length) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Message makeAlertMessage(byte[] payloadBytes) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Block makeBlock(byte[] payloadBytes) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Block makeBlock(byte[] payloadBytes, int length) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Message makeBloomFilter(byte[] payloadBytes) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public FilteredBlock makeFilteredBlock(byte[] payloadBytes) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public InventoryMessage makeInventoryMessage(byte[] payloadBytes, int length) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Transaction makeTransaction(byte[] payloadBytes, int offset, int length, byte[] hash) throws UnsupportedOperationException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public Transaction makeTransaction(byte[] payloadBytes) throws UnsupportedOperationException { + return makeTransaction(payloadBytes, 0, payloadBytes.length, null); + } + + @Override + public Transaction makeTransaction(byte[] payloadBytes, int offset) throws UnsupportedOperationException { + return makeTransaction(payloadBytes, offset, payloadBytes.length, null); + } + + @Override + public void seekPastMagicBytes(ByteBuffer in) throws BufferUnderflowException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public void serialize(String name, byte[] message, OutputStream out) throws IOException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + + @Override + public void serialize(Message message, OutputStream out) throws IOException { + throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE); + } + +} diff --git a/core/src/main/java/org/bitcoinj/core/FilteredBlock.java b/core/src/main/java/org/bitcoinj/core/FilteredBlock.java index 3a8a7d24..e34fb8e9 100644 --- a/core/src/main/java/org/bitcoinj/core/FilteredBlock.java +++ b/core/src/main/java/org/bitcoinj/core/FilteredBlock.java @@ -60,7 +60,7 @@ public class FilteredBlock extends Message { void parse() throws ProtocolException { byte[] headerBytes = new byte[Block.HEADER_SIZE]; System.arraycopy(payload, 0, headerBytes, 0, Block.HEADER_SIZE); - header = new Block(params, headerBytes); + header = params.getDefaultSerializer().makeBlock(headerBytes); merkleTree = new PartialMerkleTree(params, payload, Block.HEADER_SIZE); diff --git a/core/src/main/java/org/bitcoinj/core/GetDataMessage.java b/core/src/main/java/org/bitcoinj/core/GetDataMessage.java index 0d18c402..200ca1d8 100644 --- a/core/src/main/java/org/bitcoinj/core/GetDataMessage.java +++ b/core/src/main/java/org/bitcoinj/core/GetDataMessage.java @@ -30,17 +30,14 @@ public class GetDataMessage extends ListMessage { * Deserializes a 'getdata' message. * @param params NetworkParameters object. * @param payload Bitcoin protocol formatted byte array containing message content. - * @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 message. * @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 */ - public GetDataMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length) + public GetDataMessage(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException { - super(params, payload, parseLazy, parseRetain, length); + super(params, payload, serializer, length); } public GetDataMessage(NetworkParameters params) { diff --git a/core/src/main/java/org/bitcoinj/core/HeadersMessage.java b/core/src/main/java/org/bitcoinj/core/HeadersMessage.java index 982aaf31..213a3874 100644 --- a/core/src/main/java/org/bitcoinj/core/HeadersMessage.java +++ b/core/src/main/java/org/bitcoinj/core/HeadersMessage.java @@ -88,7 +88,8 @@ public class HeadersMessage extends Message { byte[] blockHeader = readBytes(81); if (blockHeader[80] != 0) throw new ProtocolException("Block header does not end with a null byte"); - Block newBlockHeader = new Block(this.params, blockHeader, true, true, 81); + Block newBlockHeader = this.params.getSerializer(true, true) + .makeBlock(blockHeader, 81); blockHeaders.add(newBlockHeader); } diff --git a/core/src/main/java/org/bitcoinj/core/InventoryMessage.java b/core/src/main/java/org/bitcoinj/core/InventoryMessage.java index 39a4b0ff..933979a5 100644 --- a/core/src/main/java/org/bitcoinj/core/InventoryMessage.java +++ b/core/src/main/java/org/bitcoinj/core/InventoryMessage.java @@ -37,17 +37,14 @@ public class InventoryMessage extends ListMessage { * Deserializes an 'inv' message. * @param params NetworkParameters object. * @param payload Bitcoin protocol formatted byte array containing message content. - * @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 message. * @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 */ - public InventoryMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length) + public InventoryMessage(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException { - super(params, payload, parseLazy, parseRetain, length); + super(params, payload, serializer, length); } public InventoryMessage(NetworkParameters params) { diff --git a/core/src/main/java/org/bitcoinj/core/ListMessage.java b/core/src/main/java/org/bitcoinj/core/ListMessage.java index b5d45229..e209c725 100644 --- a/core/src/main/java/org/bitcoinj/core/ListMessage.java +++ b/core/src/main/java/org/bitcoinj/core/ListMessage.java @@ -37,9 +37,9 @@ public abstract class ListMessage extends Message { super(params, bytes, 0); } - public ListMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length) + public ListMessage(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException { - super(params, payload, 0, parseLazy, parseRetain, length); + super(params, payload, 0, serializer, length); } public ListMessage(NetworkParameters params) { diff --git a/core/src/main/java/org/bitcoinj/core/Message.java b/core/src/main/java/org/bitcoinj/core/Message.java index c8b30731..90e84166 100644 --- a/core/src/main/java/org/bitcoinj/core/Message.java +++ b/core/src/main/java/org/bitcoinj/core/Message.java @@ -54,8 +54,7 @@ public abstract class Message { protected boolean parsed = false; protected boolean recached = false; - protected final boolean parseLazy; - protected final boolean parseRetain; + protected MessageSerializer serializer; protected int protocolVersion; @@ -63,19 +62,17 @@ public abstract class Message { protected Message() { parsed = true; - parseLazy = false; - parseRetain = false; + serializer = DummySerializer.DEFAULT; } Message(NetworkParameters params) { this.params = params; parsed = true; - parseLazy = false; - parseRetain = false; + serializer = params.getDefaultSerializer(); } Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion) throws ProtocolException { - this(params, payload, offset, protocolVersion, false, false, UNKNOWN_LENGTH); + this(params, payload, offset, protocolVersion, params.getDefaultSerializer(), UNKNOWN_LENGTH); } /** @@ -84,23 +81,19 @@ public abstract class Message { * @param payload Bitcoin protocol formatted byte array containing message content. * @param offset The location of the first payload byte within the array. * @param protocolVersion Bitcoin protocol version. - * @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 message. * @param length The length of message payload 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 */ - Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { - this.parseLazy = parseLazy; - this.parseRetain = parseRetain; + Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion, MessageSerializer serializer, int length) throws ProtocolException { + this.serializer = serializer; this.protocolVersion = protocolVersion; this.params = params; this.payload = payload; this.cursor = this.offset = offset; this.length = length; - if (parseLazy) { + if (serializer.isParseLazyMode()) { parseLite(); } else { parseLite(); @@ -111,13 +104,13 @@ public abstract class Message { if (this.length == UNKNOWN_LENGTH) checkState(false, "Length field has not been set in constructor for %s after %s parse. " + "Refer to Message.parseLite() for detail of required Length field contract.", - getClass().getSimpleName(), parseLazy ? "lite" : "full"); + getClass().getSimpleName(), serializer.isParseLazyMode() ? "lite" : "full"); if (SELF_CHECK) { selfCheck(payload, offset); } - if (parseRetain || !parsed) + if (serializer.isParseRetainMode() || !parsed) return; this.payload = null; } @@ -136,11 +129,11 @@ public abstract class Message { } Message(NetworkParameters params, byte[] payload, int offset) throws ProtocolException { - this(params, payload, offset, NetworkParameters.PROTOCOL_VERSION, false, false, UNKNOWN_LENGTH); + this(params, payload, offset, NetworkParameters.PROTOCOL_VERSION, params.getDefaultSerializer(), UNKNOWN_LENGTH); } - Message(NetworkParameters params, byte[] payload, int offset, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { - this(params, payload, offset, NetworkParameters.PROTOCOL_VERSION, parseLazy, parseRetain, length); + Message(NetworkParameters params, byte[] payload, int offset, MessageSerializer serializer, int length) throws ProtocolException { + this(params, payload, offset, NetworkParameters.PROTOCOL_VERSION, serializer, length); } // These methods handle the serialization/deserialization using the custom Bitcoin protocol. @@ -172,7 +165,7 @@ public abstract class Message { try { parse(); parsed = true; - if (!parseRetain) + if (!serializer.isParseRetainMode()) payload = null; } catch (ProtocolException e) { throw new LazyParseException("ProtocolException caught during lazy parse. For safe access to fields call ensureParsed before attempting read or write access", e); @@ -297,7 +290,7 @@ public abstract class Message { // Cannot happen, we are serializing to a memory stream. } - if (parseRetain) { + if (serializer.isParseRetainMode()) { // A free set of steak knives! // If there happens to be a call to this method we gain an opportunity to recache // the byte array and in this case it contains no bytes from parent messages. @@ -445,6 +438,17 @@ public abstract class Message { return params; } + /** + * Set the serializer for this message when deserialized by Java. + */ + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + if (null != params) { + this.serializer = params.getDefaultSerializer(); + } + } + public static class LazyParseException extends RuntimeException { public LazyParseException(String message, Throwable cause) { diff --git a/core/src/main/java/org/bitcoinj/core/MessageSerializer.java b/core/src/main/java/org/bitcoinj/core/MessageSerializer.java new file mode 100644 index 00000000..35736a2f --- /dev/null +++ b/core/src/main/java/org/bitcoinj/core/MessageSerializer.java @@ -0,0 +1,155 @@ +/* + * Copyright 2011 Google Inc. + * Copyright 2014 Andreas Schildbach + * Copyright 2015 Ross Nicoll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.bitcoinj.core; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +/** + * Generic interface for classes which serialize/deserialize messages. Implementing + * classes should be immutable. + */ +public interface MessageSerializer { + + /** + * Reads a message from the given ByteBuffer and returns it. + */ + Message deserialize(ByteBuffer in) throws ProtocolException, IOException, UnsupportedOperationException; + + /** + * Deserializes only the header in case packet meta data is needed before decoding + * the payload. This method assumes you have already called seekPastMagicBytes() + */ + BitcoinSerializer.BitcoinPacketHeader deserializeHeader(ByteBuffer in) throws ProtocolException, IOException, UnsupportedOperationException; + + /** + * Deserialize payload only. You must provide a header, typically obtained by calling + * {@link BitcoinSerializer#deserializeHeader}. + */ + Message deserializePayload(BitcoinSerializer.BitcoinPacketHeader header, ByteBuffer in) throws ProtocolException, BufferUnderflowException, UnsupportedOperationException; + + /** + * Whether the serializer will produce lazy parse mode Messages + */ + boolean isParseLazyMode(); + + /** + * Whether the serializer will produce cached mode Messages + */ + boolean isParseRetainMode(); + + /** + * Make an address message from the payload. Extension point for alternative + * serialization format support. + */ + AddressMessage makeAddressMessage(byte[] payloadBytes, int length) throws ProtocolException, UnsupportedOperationException; + + /** + * Make an alert message from the payload. Extension point for alternative + * serialization format support. + */ + Message makeAlertMessage(byte[] payloadBytes) throws ProtocolException, UnsupportedOperationException; + + /** + * Make a block from the payload. Extension point for alternative + * serialization format support. + */ + Block makeBlock(byte[] payloadBytes) throws ProtocolException, UnsupportedOperationException; + + /** + * Make a block from the payload. Extension point for alternative + * serialization format support. + */ + Block makeBlock(byte[] payloadBytes, int length) throws ProtocolException, UnsupportedOperationException; + + /** + * Make an filter message from the payload. Extension point for alternative + * serialization format support. + */ + Message makeBloomFilter(byte[] payloadBytes) throws ProtocolException, UnsupportedOperationException; + + /** + * Make a filtered block from the payload. Extension point for alternative + * serialization format support. + */ + FilteredBlock makeFilteredBlock(byte[] payloadBytes) throws ProtocolException, UnsupportedOperationException; + + /** + * Make an inventory message from the payload. Extension point for alternative + * serialization format support. + */ + InventoryMessage makeInventoryMessage(byte[] payloadBytes, int length) throws ProtocolException, UnsupportedOperationException; + + /** + * Make a transaction from the payload. Extension point for alternative + * serialization format support. + * + * @throws UnsupportedOperationException if this serializer/deserializer + * does not support deserialization. This can occur either because it's a dummy + * serializer (i.e. for messages with no network parameters), or because + * it does not support deserializing transactions. + */ + Transaction makeTransaction(byte[] payloadBytes, int offset, int length, byte[] hash) throws ProtocolException, UnsupportedOperationException; + + /** + * Make a transaction from the payload. Extension point for alternative + * serialization format support. + * + * @throws UnsupportedOperationException if this serializer/deserializer + * does not support deserialization. This can occur either because it's a dummy + * serializer (i.e. for messages with no network parameters), or because + * it does not support deserializing transactions. + */ + Transaction makeTransaction(byte[] payloadBytes) throws ProtocolException, UnsupportedOperationException; + + /** + * Make a transaction from the payload. Extension point for alternative + * serialization format support. + * + * @throws UnsupportedOperationException if this serializer/deserializer + * does not support deserialization. This can occur either because it's a dummy + * serializer (i.e. for messages with no network parameters), or because + * it does not support deserializing transactions. + */ + Transaction makeTransaction(byte[] payloadBytes, int offset) throws ProtocolException, UnsupportedOperationException; + + void seekPastMagicBytes(ByteBuffer in) throws BufferUnderflowException; + + /** + * Writes message to to the output stream. + * + * @throws UnsupportedOperationException if this serializer/deserializer + * does not support serialization. This can occur either because it's a dummy + * serializer (i.e. for messages with no network parameters), or because + * it does not support serializing the given message. + */ + void serialize(String name, byte[] message, OutputStream out) throws IOException, UnsupportedOperationException; + + /** + * Writes message to to the output stream. + * + * @throws UnsupportedOperationException if this serializer/deserializer + * does not support serialization. This can occur either because it's a dummy + * serializer (i.e. for messages with no network parameters), or because + * it does not support serializing the given message. + */ + void serialize(Message message, OutputStream out) throws IOException, UnsupportedOperationException; + +} diff --git a/core/src/main/java/org/bitcoinj/core/NetworkParameters.java b/core/src/main/java/org/bitcoinj/core/NetworkParameters.java index 0ad08625..256bf141 100644 --- a/core/src/main/java/org/bitcoinj/core/NetworkParameters.java +++ b/core/src/main/java/org/bitcoinj/core/NetworkParameters.java @@ -104,6 +104,7 @@ public abstract class NetworkParameters { protected int[] addrSeeds; protected HttpDiscovery.Details[] httpSeeds = {}; protected Map checkpoints = new HashMap(); + protected transient MessageSerializer defaultSerializer = null; protected NetworkParameters() { alertSigningKey = SATOSHI_KEY; @@ -417,4 +418,31 @@ public abstract class NetworkParameters { * networks. */ public abstract boolean hasMaxMoney(); + + /** + * Return the default serializer for this network. This is a shared serializer. + * @return + */ + public final MessageSerializer getDefaultSerializer() { + // Construct a default serializer if we don't have one + if (null == this.defaultSerializer) { + // Don't grab a lock unless we absolutely need it + synchronized(this) { + // Now we have a lock, double check there's still no serializer + // and create one if so. + if (null == this.defaultSerializer) { + // As the serializers are intended to be immutable, creating + // two due to a race condition should not be a problem, however + // to be safe we ensure only one exists for each network. + this.defaultSerializer = getSerializer(false, false); + } + } + } + return defaultSerializer; + } + + /** + * Construct and return a custom serializer. + */ + public abstract BitcoinSerializer getSerializer(boolean parseLazy, boolean parseRetain); } diff --git a/core/src/main/java/org/bitcoinj/core/PeerAddress.java b/core/src/main/java/org/bitcoinj/core/PeerAddress.java index afc60858..771157e2 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerAddress.java +++ b/core/src/main/java/org/bitcoinj/core/PeerAddress.java @@ -58,15 +58,11 @@ public class PeerAddress extends ChildMessage { * @param payload Bitcoin protocol formatted byte array containing message content. * @param offset The location of the first payload byte within the array. * @param protocolVersion Bitcoin protocol version. - * @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 message. * @throws ProtocolException */ - public PeerAddress(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, boolean parseLazy, - boolean parseRetain) throws ProtocolException { - super(params, payload, offset, protocolVersion, parent, parseLazy, parseRetain, UNKNOWN_LENGTH); + public PeerAddress(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, MessageSerializer serializer) throws ProtocolException { + super(params, payload, offset, protocolVersion, parent, serializer, UNKNOWN_LENGTH); // Message length is calculated in parseLite which is guaranteed to be called before it is ever read. // Even though message length is static for a PeerAddress it is safer to leave it there // as it will be set regardless of which constructor was used. diff --git a/core/src/main/java/org/bitcoinj/core/PeerSocketHandler.java b/core/src/main/java/org/bitcoinj/core/PeerSocketHandler.java index 576ae157..8907d6a0 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerSocketHandler.java +++ b/core/src/main/java/org/bitcoinj/core/PeerSocketHandler.java @@ -42,7 +42,7 @@ import static com.google.common.base.Preconditions.*; public abstract class PeerSocketHandler extends AbstractTimeoutHandler implements StreamParser { private static final Logger log = LoggerFactory.getLogger(PeerSocketHandler.class); - private final BitcoinSerializer serializer; + private final MessageSerializer serializer; protected PeerAddress peerAddress; // If we close() before we know our writeTarget, set this to true to call writeTarget.closeConnection() right away. private boolean closePending = false; @@ -59,12 +59,14 @@ public abstract class PeerSocketHandler extends AbstractTimeoutHandler implement private Lock lock = Threading.lock("PeerSocketHandler"); public PeerSocketHandler(NetworkParameters params, InetSocketAddress remoteIp) { - serializer = new BitcoinSerializer(checkNotNull(params)); + checkNotNull(params); + serializer = params.getDefaultSerializer(); this.peerAddress = new PeerAddress(remoteIp); } public PeerSocketHandler(NetworkParameters params, PeerAddress peerAddress) { - serializer = new BitcoinSerializer(checkNotNull(params)); + checkNotNull(params); + serializer = params.getDefaultSerializer(); this.peerAddress = checkNotNull(peerAddress); } diff --git a/core/src/main/java/org/bitcoinj/core/StoredBlock.java b/core/src/main/java/org/bitcoinj/core/StoredBlock.java index 58326fa1..156de483 100644 --- a/core/src/main/java/org/bitcoinj/core/StoredBlock.java +++ b/core/src/main/java/org/bitcoinj/core/StoredBlock.java @@ -138,7 +138,7 @@ public class StoredBlock { int height = buffer.getInt(); // +4 bytes byte[] header = new byte[Block.HEADER_SIZE + 1]; // Extra byte for the 00 transactions length. buffer.get(header, 0, Block.HEADER_SIZE); - return new StoredBlock(new Block(params, header), chainWork, height); + return new StoredBlock(params.getDefaultSerializer().makeBlock(header), chainWork, height); } @Override diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index f04945fe..15b21ed1 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -209,17 +209,17 @@ public class Transaction extends ChildMessage { * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH * @throws ProtocolException */ - public Transaction(NetworkParameters params, byte[] payload, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) + public Transaction(NetworkParameters params, byte[] payload, int offset, @Nullable Message parent, MessageSerializer setSerializer, int length) throws ProtocolException { - super(params, payload, offset, parent, parseLazy, parseRetain, length); + super(params, payload, offset, parent, setSerializer, length); } /** * Creates a transaction by reading payload. Length of a transaction is fixed. */ - public Transaction(NetworkParameters params, byte[] payload, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) + public Transaction(NetworkParameters params, byte[] payload, @Nullable Message parent, MessageSerializer setSerializer, int length) throws ProtocolException { - super(params, payload, 0, parent, parseLazy, parseRetain, length); + super(params, payload, 0, parent, setSerializer, length); } /** @@ -483,7 +483,7 @@ public class Transaction extends ChildMessage { protected void parseLite() throws ProtocolException { //skip this if the length has been provided i.e. the tx is not part of a block - if (parseLazy && length == UNKNOWN_LENGTH) { + if (serializer.isParseLazyMode() && length == UNKNOWN_LENGTH) { //If length hasn't been provided this tx is probably contained within a block. //In parseRetain mode the block needs to know how long the transaction is //unfortunately this requires a fairly deep (though not total) parse. @@ -554,7 +554,7 @@ public class Transaction extends ChildMessage { optimalEncodingMessageSize += VarInt.sizeOf(numInputs); inputs = new ArrayList((int) numInputs); for (long i = 0; i < numInputs; i++) { - TransactionInput input = new TransactionInput(params, this, payload, cursor, parseLazy, parseRetain); + TransactionInput input = new TransactionInput(params, this, payload, cursor, serializer); inputs.add(input); long scriptLen = readVarInt(TransactionOutPoint.MESSAGE_LENGTH); optimalEncodingMessageSize += TransactionOutPoint.MESSAGE_LENGTH + VarInt.sizeOf(scriptLen) + scriptLen + 4; @@ -565,7 +565,7 @@ public class Transaction extends ChildMessage { optimalEncodingMessageSize += VarInt.sizeOf(numOutputs); outputs = new ArrayList((int) numOutputs); for (long i = 0; i < numOutputs; i++) { - TransactionOutput output = new TransactionOutput(params, this, payload, cursor, parseLazy, parseRetain); + TransactionOutput output = new TransactionOutput(params, this, payload, cursor, serializer); outputs.add(output); long scriptLen = readVarInt(8); optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen; diff --git a/core/src/main/java/org/bitcoinj/core/TransactionInput.java b/core/src/main/java/org/bitcoinj/core/TransactionInput.java index fcc5c943..1f99a7b6 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionInput.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionInput.java @@ -116,17 +116,12 @@ public class TransactionInput extends ChildMessage { * @param params NetworkParameters object. * @param payload Bitcoin protocol formatted byte array containing message content. * @param offset The location of the first payload byte within the array. - * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. - * @param parseRetain Whether to retain the backing byte array for quick reserialization. - * 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. - * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH + * @param serializer the serializer to use for this message. * @throws ProtocolException */ - public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] payload, int offset, - boolean parseLazy, boolean parseRetain) + public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] payload, int offset, MessageSerializer serializer) throws ProtocolException { - super(params, payload, offset, parentTransaction, parseLazy, parseRetain, UNKNOWN_LENGTH); + super(params, payload, offset, parentTransaction, serializer, UNKNOWN_LENGTH); this.value = null; } @@ -140,7 +135,7 @@ public class TransactionInput extends ChildMessage { @Override void parse() throws ProtocolException { - outpoint = new TransactionOutPoint(params, payload, cursor, this, parseLazy, parseRetain); + outpoint = new TransactionOutPoint(params, payload, cursor, this, serializer); cursor += outpoint.getMessageSize(); int scriptLen = (int) readVarInt(); scriptBytes = readBytes(scriptLen); diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java b/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java index 8243b4c5..8daf4302 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java @@ -80,14 +80,11 @@ public class TransactionOutPoint extends ChildMessage { * Deserializes the message. This is usually part of a transaction message. * @param params NetworkParameters object. * @param offset The location of the first payload byte within the array. - * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. - * @param parseRetain Whether to retain the backing byte array for quick reserialization. - * 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 message. * @throws ProtocolException */ - public TransactionOutPoint(NetworkParameters params, byte[] payload, int offset, Message parent, boolean parseLazy, boolean parseRetain) throws ProtocolException { - super(params, payload, offset, parent, parseLazy, parseRetain, MESSAGE_LENGTH); + public TransactionOutPoint(NetworkParameters params, byte[] payload, int offset, Message parent, MessageSerializer serializer) throws ProtocolException { + super(params, payload, offset, parent, serializer, MESSAGE_LENGTH); } @Override diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutput.java b/core/src/main/java/org/bitcoinj/core/TransactionOutput.java index 59ec1d0a..345dca62 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionOutput.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionOutput.java @@ -69,15 +69,11 @@ public class TransactionOutput extends ChildMessage { * @param params NetworkParameters object. * @param payload Bitcoin protocol formatted byte array containing message content. * @param offset The location of the first payload byte within the array. - * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. - * @param parseRetain Whether to retain the backing byte array for quick reserialization. - * 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 message. * @throws ProtocolException */ - public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload, int offset, - boolean parseLazy, boolean parseRetain) throws ProtocolException { - super(params, payload, offset, parent, parseLazy, parseRetain, UNKNOWN_LENGTH); + public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload, int offset, MessageSerializer serializer) throws ProtocolException { + super(params, payload, offset, parent, serializer, UNKNOWN_LENGTH); availableForSpending = true; } diff --git a/core/src/main/java/org/bitcoinj/params/AbstractBitcoinNetParams.java b/core/src/main/java/org/bitcoinj/params/AbstractBitcoinNetParams.java index 74ca25ad..957702d5 100644 --- a/core/src/main/java/org/bitcoinj/params/AbstractBitcoinNetParams.java +++ b/core/src/main/java/org/bitcoinj/params/AbstractBitcoinNetParams.java @@ -31,6 +31,9 @@ import org.bitcoinj.store.BlockStoreException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static com.google.common.base.Preconditions.checkState; +import org.bitcoinj.core.BitcoinSerializer; + /** * Parameters for Bitcoin-like networks. */ @@ -133,6 +136,11 @@ public abstract class AbstractBitcoinNetParams extends NetworkParameters { return new MonetaryFormat(); } + @Override + public BitcoinSerializer getSerializer(boolean parseLazy, boolean parseRetain) { + return new BitcoinSerializer(this, parseLazy, parseRetain); + } + @Override public String getUriScheme() { return BITCOIN_SCHEME; diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClient.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClient.java index f6096473..5b83aed7 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClient.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClient.java @@ -373,7 +373,7 @@ public class PaymentChannelClient implements IPaymentChannelClient { private void receiveClose(Protos.TwoWayChannelMessage msg) throws VerificationException { checkState(lock.isHeldByCurrentThread()); if (msg.hasSettlement()) { - Transaction settleTx = new Transaction(wallet.getParams(), msg.getSettlement().getTx().toByteArray()); + Transaction settleTx = wallet.getParams().getDefaultSerializer().makeTransaction(msg.getSettlement().getTx().toByteArray()); log.info("CLOSE message received with settlement tx {}", settleTx.getHash()); // TODO: set source if (state != null && state().isSettlementTransaction(settleTx)) { diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServer.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServer.java index 6db3178b..97272e32 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServer.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServer.java @@ -295,7 +295,7 @@ public class PaymentChannelServer { Protos.ProvideRefund providedRefund = msg.getProvideRefund(); state = new PaymentChannelServerState(broadcaster, wallet, myKey, expireTime); - byte[] signature = state.provideRefundTransaction(new Transaction(wallet.getParams(), providedRefund.getTx().toByteArray()), + byte[] signature = state.provideRefundTransaction(wallet.getParams().getDefaultSerializer().makeTransaction(providedRefund.getTx().toByteArray()), providedRefund.getMultisigKey().toByteArray()); step = InitStep.WAITING_ON_CONTRACT; @@ -349,7 +349,7 @@ public class PaymentChannelServer { final Protos.ProvideContract providedContract = msg.getProvideContract(); //TODO notify connection handler that timeout should be significantly extended as we wait for network propagation? - final Transaction multisigContract = new Transaction(wallet.getParams(), providedContract.getTx().toByteArray()); + final Transaction multisigContract = wallet.getParams().getDefaultSerializer().makeTransaction(providedContract.getTx().toByteArray()); step = InitStep.WAITING_ON_MULTISIG_ACCEPTANCE; state.provideMultiSigContract(multisigContract) .addListener(new Runnable() { diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelClientStates.java b/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelClientStates.java index 94b9b6fb..2c38e002 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelClientStates.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelClientStates.java @@ -302,13 +302,13 @@ public class StoredPaymentChannelClientStates implements WalletExtension { NetworkParameters params = containingWallet.getParams(); ClientState.StoredClientPaymentChannels states = ClientState.StoredClientPaymentChannels.parseFrom(data); for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) { - Transaction refundTransaction = new Transaction(params, storedState.getRefundTransaction().toByteArray()); + Transaction refundTransaction = params.getDefaultSerializer().makeTransaction(storedState.getRefundTransaction().toByteArray()); refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF); ECKey myKey = (storedState.getMyKey().isEmpty()) ? containingWallet.findKeyFromPubKey(storedState.getMyPublicKey().toByteArray()) : ECKey.fromPrivate(storedState.getMyKey().toByteArray()); StoredClientChannel channel = new StoredClientChannel(Sha256Hash.wrap(storedState.getId().toByteArray()), - new Transaction(params, storedState.getContractTransaction().toByteArray()), + params.getDefaultSerializer().makeTransaction(storedState.getContractTransaction().toByteArray()), refundTransaction, myKey, Coin.valueOf(storedState.getValueToMe()), diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelServerStates.java b/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelServerStates.java index 9d46c5ad..e3485de1 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelServerStates.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/StoredPaymentChannelServerStates.java @@ -232,7 +232,7 @@ public class StoredPaymentChannelServerStates implements WalletExtension { NetworkParameters params = containingWallet.getParams(); for (ServerState.StoredServerPaymentChannel storedState : states.getChannelsList()) { StoredServerChannel channel = new StoredServerChannel(null, - new Transaction(params, storedState.getContractTransaction().toByteArray()), + params.getDefaultSerializer().makeTransaction(storedState.getContractTransaction().toByteArray()), new TransactionOutput(params, null, storedState.getClientOutput().toByteArray(), 0), storedState.getRefundTransactionUnlockTimeSecs(), ECKey.fromPrivate(storedState.getMyKey().toByteArray()), diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java index da0728fa..8fafed63 100644 --- a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java +++ b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentProtocol.java @@ -342,7 +342,7 @@ public class PaymentProtocol { Protos.Payment paymentMessage) { final List transactions = new ArrayList(paymentMessage.getTransactionsCount()); for (final ByteString transaction : paymentMessage.getTransactionsList()) - transactions.add(new Transaction(params, transaction.toByteArray())); + transactions.add(params.getDefaultSerializer().makeTransaction(transaction.toByteArray())); return transactions; } diff --git a/core/src/main/java/org/bitcoinj/script/Script.java b/core/src/main/java/org/bitcoinj/script/Script.java index bcca25e1..6dc2d0bc 100644 --- a/core/src/main/java/org/bitcoinj/script/Script.java +++ b/core/src/main/java/org/bitcoinj/script/Script.java @@ -1413,7 +1413,7 @@ public class Script { // Clone the transaction because executing the script involves editing it, and if we die, we'll leave // the tx half broken (also it's not so thread safe to work on it directly. try { - txContainingThis = new Transaction(txContainingThis.getParams(), txContainingThis.bitcoinSerialize()); + txContainingThis = txContainingThis.getParams().getDefaultSerializer().makeTransaction(txContainingThis.bitcoinSerialize()); } catch (ProtocolException e) { throw new RuntimeException(e); // Should not happen unless we were given a totally broken transaction. } diff --git a/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java index 5b836813..8e98ad98 100644 --- a/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java @@ -749,7 +749,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto BigInteger chainWork = new BigInteger(results.getBytes(1)); int height = results.getInt(2); - Block b = new Block(params, results.getBytes(3)); + Block b = params.getDefaultSerializer().makeBlock(results.getBytes(3)); b.verifyHeader(); StoredBlock stored = new StoredBlock(b, chainWork, height); return stored; @@ -811,7 +811,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto ((transactions[offset++] & 0xFF) << 24); List transactionList = new LinkedList(); for (int i = 0; i < numTxn; i++) { - Transaction tx = new Transaction(params, transactions, offset); + Transaction tx = params.getDefaultSerializer().makeTransaction(transactions, offset); transactionList.add(tx); offset += tx.getMessageSize(); } diff --git a/core/src/main/java/org/bitcoinj/testing/FakeTxBuilder.java b/core/src/main/java/org/bitcoinj/testing/FakeTxBuilder.java index 01f2b4f1..0ce3a9a7 100644 --- a/core/src/main/java/org/bitcoinj/testing/FakeTxBuilder.java +++ b/core/src/main/java/org/bitcoinj/testing/FakeTxBuilder.java @@ -116,7 +116,7 @@ public class FakeTxBuilder { */ public static Transaction roundTripTransaction(NetworkParameters params, Transaction tx) { try { - BitcoinSerializer bs = new BitcoinSerializer(params); + MessageSerializer bs = params.getDefaultSerializer(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); bs.serialize(tx, bos); return (Transaction) bs.deserialize(ByteBuffer.wrap(bos.toByteArray())); @@ -153,8 +153,8 @@ public class FakeTxBuilder { doubleSpends.t2.addOutput(o2); try { - doubleSpends.t1 = new Transaction(params, doubleSpends.t1.bitcoinSerialize()); - doubleSpends.t2 = new Transaction(params, doubleSpends.t2.bitcoinSerialize()); + doubleSpends.t1 = params.getDefaultSerializer().makeTransaction(doubleSpends.t1.bitcoinSerialize()); + doubleSpends.t2 = params.getDefaultSerializer().makeTransaction(doubleSpends.t2.bitcoinSerialize()); } catch (ProtocolException e) { throw new RuntimeException(e); } diff --git a/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java b/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java index 1b210319..058ad289 100644 --- a/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java +++ b/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java @@ -150,7 +150,7 @@ public class BlockFileLoader implements Iterable, Iterator { bytes = new byte[(int) size]; currentFileStream.read(bytes, 0, (int) size); try { - nextBlock = new Block(params, bytes); + nextBlock = params.getDefaultSerializer().makeBlock(bytes); } catch (ProtocolException e) { nextBlock = null; continue; diff --git a/core/src/test/java/org/bitcoinj/core/BitcoinSerializerTest.java b/core/src/test/java/org/bitcoinj/core/BitcoinSerializerTest.java index 764abfda..6d9f325d 100644 --- a/core/src/test/java/org/bitcoinj/core/BitcoinSerializerTest.java +++ b/core/src/test/java/org/bitcoinj/core/BitcoinSerializerTest.java @@ -55,7 +55,7 @@ public class BitcoinSerializerTest { @Test public void testAddr() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); + MessageSerializer bs = MainNetParams.get().getDefaultSerializer(); // the actual data from https://en.bitcoin.it/wiki/Protocol_specification#addr AddressMessage a = (AddressMessage)bs.deserialize(ByteBuffer.wrap(addrMessage)); assertEquals(1, a.getAddresses().size()); @@ -77,7 +77,7 @@ public class BitcoinSerializerTest { @Test public void testLazyParsing() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get(), true, false); + MessageSerializer bs = MainNetParams.get().getSerializer(true, false); Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); assertNotNull(tx); @@ -98,7 +98,7 @@ public class BitcoinSerializerTest { } private void testCachedParsing(boolean lazy) throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get(), lazy, true); + MessageSerializer bs = MainNetParams.get().getSerializer(lazy, true); //first try writing to a fields to ensure uncaching and children are not affected Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); @@ -161,7 +161,7 @@ public class BitcoinSerializerTest { */ @Test public void testHeaders1() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); + MessageSerializer bs = MainNetParams.get().getDefaultSerializer(); String headersMessageBytesHex = "f9beb4d9686561" + "646572730000000000520000005d4fab8101010000006fe28c0ab6f1b372c1a6a246ae6" + @@ -195,7 +195,7 @@ public class BitcoinSerializerTest { * Get 6 headers of blocks 1-6 in the chain */ public void testHeaders2() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); + MessageSerializer bs = MainNetParams.get().getDefaultSerializer(); String headersMessageBytesHex = "f9beb4d96865616465" + "72730000000000e701000085acd4ea06010000006fe28c0ab6f1b372c1a6a246ae63f74f931e" + @@ -266,7 +266,7 @@ public class BitcoinSerializerTest { // Fail in another way, there is data in the stream but no magic bytes. byte[] brokenMessage = HEX.decode("000000"); try { - new BitcoinSerializer(MainNetParams.get()).seekPastMagicBytes(ByteBuffer.wrap(brokenMessage)); + MainNetParams.get().getDefaultSerializer().seekPastMagicBytes(ByteBuffer.wrap(brokenMessage)); fail(); } catch (BufferUnderflowException e) { // expected @@ -278,7 +278,7 @@ public class BitcoinSerializerTest { * Tests serialization of an unknown message. */ public void testSerializeUnknownMessage() { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); + MessageSerializer bs = MainNetParams.get().getDefaultSerializer(); UnknownMessage a = new UnknownMessage(); ByteArrayOutputStream bos = new ByteArrayOutputStream(addrMessage.length); diff --git a/core/src/test/java/org/bitcoinj/core/BlockTest.java b/core/src/test/java/org/bitcoinj/core/BlockTest.java index 6e488cdd..b72d7e22 100644 --- a/core/src/test/java/org/bitcoinj/core/BlockTest.java +++ b/core/src/test/java/org/bitcoinj/core/BlockTest.java @@ -56,7 +56,7 @@ public class BlockTest { @Test public void testBlockVerification() throws Exception { - Block block = new Block(params, blockBytes); + Block block = params.getDefaultSerializer().makeBlock(blockBytes); block.verify(); assertEquals("00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.getHashAsString()); } @@ -64,7 +64,7 @@ public class BlockTest { @SuppressWarnings("deprecation") @Test public void testDate() throws Exception { - Block block = new Block(params, blockBytes); + Block block = params.getDefaultSerializer().makeBlock(blockBytes); assertEquals("4 Nov 2010 16:06:04 GMT", block.getTime().toGMTString()); } @@ -72,7 +72,7 @@ public class BlockTest { public void testProofOfWork() throws Exception { // This params accepts any difficulty target. NetworkParameters params = UnitTestParams.get(); - Block block = new Block(params, blockBytes); + Block block = params.getDefaultSerializer().makeBlock(blockBytes); block.setNonce(12346); try { block.verify(); @@ -101,7 +101,7 @@ public class BlockTest { @Test public void testBadTransactions() throws Exception { - Block block = new Block(params, blockBytes); + Block block = params.getDefaultSerializer().makeBlock(blockBytes); // Re-arrange so the coinbase transaction is not first. Transaction tx1 = block.transactions.get(0); Transaction tx2 = block.transactions.get(1); @@ -117,9 +117,9 @@ public class BlockTest { @Test public void testHeaderParse() throws Exception { - Block block = new Block(params, blockBytes); + Block block = params.getDefaultSerializer().makeBlock(blockBytes); Block header = block.cloneAsHeader(); - Block reparsed = new Block(params, header.bitcoinSerialize()); + Block reparsed = params.getDefaultSerializer().makeBlock(header.bitcoinSerialize()); assertEquals(reparsed, header); } @@ -129,7 +129,7 @@ public class BlockTest { // proves that transaction serialization works, along with all its subobjects like scripts and in/outpoints. // // NB: This tests the bitcoin serialization protocol. - Block block = new Block(params, blockBytes); + Block block = params.getDefaultSerializer().makeBlock(blockBytes); assertTrue(Arrays.equals(blockBytes, block.bitcoinSerialize())); } diff --git a/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java b/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java index 5fc7a48c..cd5e0ea4 100644 --- a/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java +++ b/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java @@ -267,7 +267,7 @@ public class ChainSplitTest { } private Block roundtrip(Block b2) throws ProtocolException { - return new Block(unitTestParams, b2.bitcoinSerialize()); + return unitTestParams.getDefaultSerializer().makeBlock(b2.bitcoinSerialize()); } @Test diff --git a/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java b/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java index 22d71b95..0c441291 100644 --- a/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java +++ b/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java @@ -59,7 +59,7 @@ public class CoinbaseBlockTest { byte[] blockAsBytes = getBytes(getClass().getResourceAsStream("block169482.dat")); // Create block 169482. - Block block = new Block(params, blockAsBytes); + Block block = params.getDefaultSerializer().makeBlock(blockAsBytes); // Check block. assertNotNull(block); diff --git a/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java b/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java index 736758cb..1de2e143 100644 --- a/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java +++ b/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java @@ -139,20 +139,20 @@ public class FilteredBlockAndPartialMerkleTreeTests extends TestWithPeerGroup { List txHashList = filteredBlock.getTransactionHashes(); assertTrue(txHashList.size() == 4); // Four transactions (0, 1, 2, 6) from block 100001 - Transaction tx0 = new Transaction(params, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b010dffffffff0100f2052a01000000434104b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63ac00000000")); + Transaction tx0 = params.getDefaultSerializer().makeTransaction(HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b010dffffffff0100f2052a01000000434104b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63ac00000000")); assertTrue(tx0.getHash().equals(Sha256Hash.wrap("bb28a1a5b3a02e7657a81c38355d56c6f05e80b9219432e3352ddcfc3cb6304c"))); assertEquals(tx0.getHash(), txHashList.get(0)); - Transaction tx1 = new Transaction(params, HEX.decode("0100000001d992e5a888a86d4c7a6a69167a4728ee69497509740fc5f456a24528c340219a000000008b483045022100f0519bdc9282ff476da1323b8ef7ffe33f495c1a8d52cc522b437022d83f6a230220159b61d197fbae01b4a66622a23bc3f1def65d5fa24efd5c26fa872f3a246b8e014104839f9023296a1fabb133140128ca2709f6818c7d099491690bd8ac0fd55279def6a2ceb6ab7b5e4a71889b6e739f09509565eec789e86886f6f936fa42097adeffffffff02000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac00e32321000000001976a9140c34f4e29ab5a615d5ea28d4817f12b137d62ed588ac00000000")); + Transaction tx1 = params.getDefaultSerializer().makeTransaction(HEX.decode("0100000001d992e5a888a86d4c7a6a69167a4728ee69497509740fc5f456a24528c340219a000000008b483045022100f0519bdc9282ff476da1323b8ef7ffe33f495c1a8d52cc522b437022d83f6a230220159b61d197fbae01b4a66622a23bc3f1def65d5fa24efd5c26fa872f3a246b8e014104839f9023296a1fabb133140128ca2709f6818c7d099491690bd8ac0fd55279def6a2ceb6ab7b5e4a71889b6e739f09509565eec789e86886f6f936fa42097adeffffffff02000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac00e32321000000001976a9140c34f4e29ab5a615d5ea28d4817f12b137d62ed588ac00000000")); assertTrue(tx1.getHash().equals(Sha256Hash.wrap("fbde5d03b027d2b9ba4cf5d4fecab9a99864df2637b25ea4cbcb1796ff6550ca"))); assertEquals(tx1.getHash(), txHashList.get(1)); - Transaction tx2 = new Transaction(params, HEX.decode("01000000059daf0abe7a92618546a9dbcfd65869b6178c66ec21ccfda878c1175979cfd9ef000000004a493046022100c2f7f25be5de6ce88ac3c1a519514379e91f39b31ddff279a3db0b1a229b708b022100b29efbdbd9837cc6a6c7318aa4900ed7e4d65662c34d1622a2035a3a5534a99a01ffffffffd516330ebdf075948da56db13d22632a4fb941122df2884397dda45d451acefb0000000048473044022051243debe6d4f2b433bee0cee78c5c4073ead0e3bde54296dbed6176e128659c022044417bfe16f44eb7b6eb0cdf077b9ce972a332e15395c09ca5e4f602958d266101ffffffffe1f5aa33961227b3c344e57179417ce01b7ccd421117fe2336289b70489883f900000000484730440220593252bb992ce3c85baf28d6e3aa32065816271d2c822398fe7ee28a856bc943022066d429dd5025d3c86fd8fd8a58e183a844bd94aa312cefe00388f57c85b0ca3201ffffffffe207e83718129505e6a7484831442f668164ae659fddb82e9e5421a081fb90d50000000049483045022067cf27eb733e5bcae412a586b25a74417c237161a084167c2a0b439abfebdcb2022100efcc6baa6824b4c5205aa967e0b76d31abf89e738d4b6b014e788c9a8cccaf0c01ffffffffe23b8d9d80a9e9d977fab3c94dbe37befee63822443c3ec5ae5a713ede66c3940000000049483045022020f2eb35036666b1debe0d1d2e77a36d5d9c4e96c1dba23f5100f193dbf524790221008ce79bc1321fb4357c6daee818038d41544749127751726e46b2b320c8b565a201ffffffff0200ba1dd2050000001976a914366a27645806e817a6cd40bc869bdad92fe5509188ac40420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac00000000")); + Transaction tx2 = params.getDefaultSerializer().makeTransaction(HEX.decode("01000000059daf0abe7a92618546a9dbcfd65869b6178c66ec21ccfda878c1175979cfd9ef000000004a493046022100c2f7f25be5de6ce88ac3c1a519514379e91f39b31ddff279a3db0b1a229b708b022100b29efbdbd9837cc6a6c7318aa4900ed7e4d65662c34d1622a2035a3a5534a99a01ffffffffd516330ebdf075948da56db13d22632a4fb941122df2884397dda45d451acefb0000000048473044022051243debe6d4f2b433bee0cee78c5c4073ead0e3bde54296dbed6176e128659c022044417bfe16f44eb7b6eb0cdf077b9ce972a332e15395c09ca5e4f602958d266101ffffffffe1f5aa33961227b3c344e57179417ce01b7ccd421117fe2336289b70489883f900000000484730440220593252bb992ce3c85baf28d6e3aa32065816271d2c822398fe7ee28a856bc943022066d429dd5025d3c86fd8fd8a58e183a844bd94aa312cefe00388f57c85b0ca3201ffffffffe207e83718129505e6a7484831442f668164ae659fddb82e9e5421a081fb90d50000000049483045022067cf27eb733e5bcae412a586b25a74417c237161a084167c2a0b439abfebdcb2022100efcc6baa6824b4c5205aa967e0b76d31abf89e738d4b6b014e788c9a8cccaf0c01ffffffffe23b8d9d80a9e9d977fab3c94dbe37befee63822443c3ec5ae5a713ede66c3940000000049483045022020f2eb35036666b1debe0d1d2e77a36d5d9c4e96c1dba23f5100f193dbf524790221008ce79bc1321fb4357c6daee818038d41544749127751726e46b2b320c8b565a201ffffffff0200ba1dd2050000001976a914366a27645806e817a6cd40bc869bdad92fe5509188ac40420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac00000000")); assertTrue(tx2.getHash().equals(Sha256Hash.wrap("8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb"))); assertEquals(tx2.getHash(), txHashList.get(2)); - Transaction tx3 = new Transaction(params, HEX.decode("01000000011b56cf3aab3286d582c055a42af3a911ee08423f276da702bb67f1222ac1a5b6000000008c4930460221009e9fba682e162c9627b96b7df272006a727988680b956c61baff869f0907b8fb022100a9c19adc7c36144bafe526630783845e5cb9554d30d3edfb56f0740274d507f30141046e0efbfac7b1615ad553a6f097615bc63b7cdb3b8e1cb3263b619ba63740012f51c7c5b09390e3577e377b7537e61226e315f95f926444fc5e5f2978c112e448ffffffff02c0072b11010000001976a914b73e9e01933351ca076faf8e0d94dd58079d0b1f88ac80b63908000000001976a9141aca0bdf0d2cee63db19aa4a484f45a4e26a880c88ac00000000")); + Transaction tx3 = params.getDefaultSerializer().makeTransaction(HEX.decode("01000000011b56cf3aab3286d582c055a42af3a911ee08423f276da702bb67f1222ac1a5b6000000008c4930460221009e9fba682e162c9627b96b7df272006a727988680b956c61baff869f0907b8fb022100a9c19adc7c36144bafe526630783845e5cb9554d30d3edfb56f0740274d507f30141046e0efbfac7b1615ad553a6f097615bc63b7cdb3b8e1cb3263b619ba63740012f51c7c5b09390e3577e377b7537e61226e315f95f926444fc5e5f2978c112e448ffffffff02c0072b11010000001976a914b73e9e01933351ca076faf8e0d94dd58079d0b1f88ac80b63908000000001976a9141aca0bdf0d2cee63db19aa4a484f45a4e26a880c88ac00000000")); assertTrue(tx3.getHash().equals(Sha256Hash.wrap("c5abc61566dbb1c4bce5e1fda7b66bed22eb2130cea4b721690bc1488465abc9"))); assertEquals(tx3.getHash(),txHashList.get(3)); diff --git a/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java b/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java index c6c93dec..eb598a93 100644 --- a/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java +++ b/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java @@ -1058,7 +1058,7 @@ public class FullBlockTestGenerator { Block b56; try { - b56 = new Block(params, b57.block.bitcoinSerialize()); + b56 = params.getDefaultSerializer().makeBlock(b57.block.bitcoinSerialize()); } catch (ProtocolException e) { throw new RuntimeException(e); // Cannot happen. } @@ -1099,7 +1099,7 @@ public class FullBlockTestGenerator { Block b56p2; try { - b56p2 = new Block(params, b57p2.block.bitcoinSerialize()); + b56p2 = params.getDefaultSerializer().makeBlock(b57p2.block.bitcoinSerialize()); } catch (ProtocolException e) { throw new RuntimeException(e); // Cannot happen. } @@ -1218,7 +1218,7 @@ public class FullBlockTestGenerator { for (Transaction transaction : b64Original.block.getTransactions()) transaction.bitcoinSerialize(stream); - b64 = new Block(params, stream.toByteArray(), false, true, stream.size()); + b64 = params.getSerializer(false, true).makeBlock(stream.toByteArray(), stream.size()); // The following checks are checking to ensure block serialization functions in the way needed for this test // If they fail, it is likely not an indication of error, but an indication that this test needs rewritten @@ -1346,7 +1346,7 @@ public class FullBlockTestGenerator { } b72.solve(); - Block b71 = new Block(params, b72.block.bitcoinSerialize()); + Block b71 = params.getDefaultSerializer().makeBlock(b72.block.bitcoinSerialize()); b71.addTransaction(b72.block.getTransactions().get(2)); checkState(b71.getHash().equals(b72.getHash())); blocks.add(new BlockAndValidity(b71, false, true, b69.getHash(), chainHeadHeight + 21, "b71")); diff --git a/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java b/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java index bb7f5b03..aab92661 100644 --- a/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java +++ b/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java @@ -103,7 +103,7 @@ public class LazyParseByteCacheTest { Block b1 = createFakeBlock(blockStore, tx1, tx2).block; - BitcoinSerializer bs = new BitcoinSerializer(unitTestParams); + MessageSerializer bs = unitTestParams.getDefaultSerializer(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); bs.serialize(tx1, bos); @@ -175,10 +175,10 @@ public class LazyParseByteCacheTest { public void testBlock(byte[] blockBytes, boolean isChild, boolean lazy, boolean retain) throws Exception { //reference serializer to produce comparison serialization output after changes to //message structure. - BitcoinSerializer bsRef = new BitcoinSerializer(unitTestParams, false, false); + MessageSerializer bsRef = unitTestParams.getSerializer(false, false); ByteArrayOutputStream bos = new ByteArrayOutputStream(); - BitcoinSerializer bs = new BitcoinSerializer(unitTestParams, lazy, retain); + BitcoinSerializer bs = unitTestParams.getSerializer(lazy, retain); Block b1; Block bRef; b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); @@ -411,10 +411,10 @@ public class LazyParseByteCacheTest { //reference serializer to produce comparison serialization output after changes to //message structure. - BitcoinSerializer bsRef = new BitcoinSerializer(params, false, false); + MessageSerializer bsRef = params.getSerializer(false, false); ByteArrayOutputStream bos = new ByteArrayOutputStream(); - BitcoinSerializer bs = new BitcoinSerializer(params, lazy, retain); + BitcoinSerializer bs = params.getSerializer(lazy, retain); Transaction t1; Transaction tRef; t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes)); @@ -479,7 +479,7 @@ public class LazyParseByteCacheTest { } - private void serDeser(BitcoinSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception { + private void serDeser(MessageSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bs.serialize(message, bos); byte[] b1 = bos.toByteArray(); diff --git a/core/src/test/java/org/bitcoinj/core/PeerTest.java b/core/src/test/java/org/bitcoinj/core/PeerTest.java index 84ee6646..e06ced53 100644 --- a/core/src/test/java/org/bitcoinj/core/PeerTest.java +++ b/core/src/test/java/org/bitcoinj/core/PeerTest.java @@ -863,7 +863,7 @@ public class PeerTest extends TestWithNetworkConnections { } }); final NetworkParameters params = TestNet3Params.get(); - BitcoinSerializer serializer = new BitcoinSerializer(params); + MessageSerializer serializer = params.getDefaultSerializer(); // Now write some bogus truncated message. ByteArrayOutputStream out = new ByteArrayOutputStream(); serializer.serialize("inv", new InventoryMessage(params) { diff --git a/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java b/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java index b73ed4a5..a714974b 100644 --- a/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java +++ b/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java @@ -52,7 +52,7 @@ public class TxConfidenceTableTest { @Test public void pinHandlers() throws Exception { - Transaction tx = new Transaction(params, tx1.bitcoinSerialize()); + Transaction tx = params.getDefaultSerializer().makeTransaction(tx1.bitcoinSerialize()); Sha256Hash hash = tx.getHash(); table.seen(hash, address1); assertEquals(1, tx.getConfidence().numBroadcastPeers()); diff --git a/core/src/test/java/org/bitcoinj/core/WalletTest.java b/core/src/test/java/org/bitcoinj/core/WalletTest.java index f52c2b34..d82839bf 100644 --- a/core/src/test/java/org/bitcoinj/core/WalletTest.java +++ b/core/src/test/java/org/bitcoinj/core/WalletTest.java @@ -624,7 +624,7 @@ public class WalletTest extends TestWithWallet { // Send 0.10 to somebody else. Transaction send1 = wallet.createSend(new ECKey().toAddress(params), valueOf(0, 10)); // Reserialize. - Transaction send2 = new Transaction(params, send1.bitcoinSerialize()); + Transaction send2 = params.getDefaultSerializer().makeTransaction(send1.bitcoinSerialize()); assertEquals(nanos, send2.getValueSentFromMe(wallet)); assertEquals(ZERO.subtract(valueOf(0, 10)), send2.getValue(wallet)); } @@ -641,7 +641,7 @@ public class WalletTest extends TestWithWallet { assertTrue("Wallet is not consistent", wallet.isConsistent()); - Transaction txClone = new Transaction(params, tx.bitcoinSerialize()); + Transaction txClone = params.getDefaultSerializer().makeTransaction(tx.bitcoinSerialize()); try { wallet.receiveFromBlock(txClone, null, BlockChain.NewBlockType.BEST_CHAIN, 0); fail("Illegal argument not thrown when it should have been."); @@ -737,7 +737,7 @@ public class WalletTest extends TestWithWallet { Transaction send1 = wallet.createSend(new ECKey().toAddress(params), valueOf(2, 90)); // Create a double spend of just the first one. Transaction send2 = wallet.createSend(new ECKey().toAddress(params), COIN); - send2 = new Transaction(params, send2.bitcoinSerialize()); + send2 = params.getDefaultSerializer().makeTransaction(send2.bitcoinSerialize()); // Broadcast send1, it's now pending. wallet.commitTx(send1); assertEquals(ZERO, wallet.getBalance()); @@ -765,7 +765,7 @@ public class WalletTest extends TestWithWallet { Transaction send2 = checkNotNull(wallet.createSend(address, value2)); byte[] buf = send1.bitcoinSerialize(); buf[43] = 0; // Break the signature: bitcoinj won't check in SPV mode and this is easier than other mutations. - send1 = new Transaction(params, buf); + send1 = params.getDefaultSerializer().makeTransaction(buf); wallet.commitTx(send2); wallet.allowSpendingUnconfirmedTransactions(); assertEquals(value, wallet.getBalance(Wallet.BalanceType.ESTIMATED)); @@ -830,7 +830,7 @@ public class WalletTest extends TestWithWallet { Transaction send1 = wallet.createSend(new ECKey().toAddress(params), valueOf(0, 50)); // Create a double spend. Transaction send2 = wallet.createSend(new ECKey().toAddress(params), valueOf(0, 50)); - send2 = new Transaction(params, send2.bitcoinSerialize()); + send2 = params.getDefaultSerializer().makeTransaction(send2.bitcoinSerialize()); // Broadcast send1. wallet.commitTx(send1); assertEquals(send1, received.getOutput(0).getSpentBy().getParentTransaction()); @@ -912,7 +912,7 @@ public class WalletTest extends TestWithWallet { wallet.notifyNewBestBlock(createFakeBlock(blockStore).storedBlock); Threading.waitForUserCode(); assertNull(reasons[0]); - final Transaction t1Copy = new Transaction(params, t1.bitcoinSerialize()); + final Transaction t1Copy = params.getDefaultSerializer().makeTransaction(t1.bitcoinSerialize()); sendMoneyToWallet(t1Copy, AbstractBlockChain.NewBlockType.BEST_CHAIN); Threading.waitForUserCode(); assertFalse(flags[0]); diff --git a/core/src/test/java/org/bitcoinj/script/ScriptTest.java b/core/src/test/java/org/bitcoinj/script/ScriptTest.java index c082dfb9..0467eaee 100644 --- a/core/src/test/java/org/bitcoinj/script/ScriptTest.java +++ b/core/src/test/java/org/bitcoinj/script/ScriptTest.java @@ -125,7 +125,7 @@ public class ScriptTest { ECKey key3 = DumpedPrivateKey.fromBase58(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey(); Script multisigScript = ScriptBuilder.createMultiSigOutputScript(2, Arrays.asList(key1, key2, key3)); byte[] bytes = HEX.decode("01000000013df681ff83b43b6585fa32dd0e12b0b502e6481e04ee52ff0fdaf55a16a4ef61000000006b483045022100a84acca7906c13c5895a1314c165d33621cdcf8696145080895cbf301119b7cf0220730ff511106aa0e0a8570ff00ee57d7a6f24e30f592a10cae1deffac9e13b990012102b8d567bcd6328fd48a429f9cf4b315b859a58fd28c5088ef3cb1d98125fc4e8dffffffff02364f1c00000000001976a91439a02793b418de8ec748dd75382656453dc99bcb88ac40420f000000000017a9145780b80be32e117f675d6e0ada13ba799bf248e98700000000"); - Transaction transaction = new Transaction(params, bytes); + Transaction transaction = params.getDefaultSerializer().makeTransaction(bytes); TransactionOutput output = transaction.getOutput(1); Transaction spendTx = new Transaction(params); Address address = Address.fromBase58(params, "n3CFiCmBXVt5d3HXKQ15EFZyhPz4yj5F3H"); @@ -324,7 +324,7 @@ public class ScriptTest { Transaction transaction = null; try { Map scriptPubKeys = parseScriptPubKeys(test.get(0)); - transaction = new Transaction(params, HEX.decode(test.get(1).asText().toLowerCase())); + transaction = params.getDefaultSerializer().makeTransaction(HEX.decode(test.get(1).asText().toLowerCase())); transaction.verify(); Set verifyFlags = parseVerifyFlags(test.get(2).asText()); @@ -353,7 +353,7 @@ public class ScriptTest { if (test.isArray() && test.size() == 1 && test.get(0).isTextual()) continue; // This is a comment. Map scriptPubKeys = parseScriptPubKeys(test.get(0)); - Transaction transaction = new Transaction(params, HEX.decode(test.get(1).asText().toLowerCase())); + Transaction transaction = params.getDefaultSerializer().makeTransaction(HEX.decode(test.get(1).asText().toLowerCase())); Set verifyFlags = parseVerifyFlags(test.get(2).asText()); boolean valid = true; diff --git a/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java b/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java index 68f317fc..c245d8c6 100644 --- a/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java +++ b/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java @@ -188,7 +188,7 @@ public class WalletProtobufSerializerTest { assertTrue(lastSeenBlockHash.isEmpty()); // Create a block. - Block block = new Block(params, BlockTest.blockBytes); + Block block = params.getDefaultSerializer().makeBlock(BlockTest.blockBytes); Sha256Hash blockHash = block.getHash(); wallet.setLastBlockSeenHash(blockHash); wallet.setLastBlockSeenHeight(1);