diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index 731112f13..b6307361d 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -679,7 +679,7 @@ public class Transaction extends Message { int numInputs = numInputsVarInt.intValue(); inputs = new ArrayList<>(Math.min((int) numInputs, Utils.MAX_INITIAL_ARRAY_LENGTH)); for (long i = 0; i < numInputs; i++) { - TransactionInput input = new TransactionInput(this, payload.slice()); + TransactionInput input = TransactionInput.read(payload.slice(), this); inputs.add(input); // intentionally read again, due to the slice above Buffers.skipBytes(payload, TransactionOutPoint.BYTES); @@ -1525,7 +1525,7 @@ public class Transaction extends Message { // txin_count, txins stream.write(VarInt.of(inputs.size()).serialize()); for (TransactionInput in : inputs) - in.bitcoinSerializeToStream(stream); + stream.write(in.serialize()); // txout_count, txouts stream.write(VarInt.of(outputs.size()).serialize()); for (TransactionOutput out : outputs) diff --git a/core/src/main/java/org/bitcoinj/core/TransactionInput.java b/core/src/main/java/org/bitcoinj/core/TransactionInput.java index 4fd9d219b..b4d740018 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionInput.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionInput.java @@ -30,9 +30,8 @@ import org.bitcoinj.wallet.KeyBag; import org.bitcoinj.wallet.RedeemData; import javax.annotation.Nullable; -import java.io.IOException; -import java.io.OutputStream; import java.lang.ref.WeakReference; +import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -49,7 +48,7 @@ import static org.bitcoinj.base.internal.Preconditions.checkArgument; * *
Instances of this class are not safe for use by multiple threads.
*/ -public class TransactionInput extends Message { +public class TransactionInput { /** Magic sequence number that indicates there is no sequence number. */ public static final long NO_SEQUENCE = 0xFFFFFFFFL; /** @@ -103,17 +102,37 @@ public class TransactionInput extends Message { return new TransactionInput(parentTransaction, scriptBytes, TransactionOutPoint.UNCONNECTED); } - public TransactionInput(@Nullable Transaction parentTransaction, byte[] scriptBytes, - TransactionOutPoint outpoint) { - this(parentTransaction, scriptBytes, outpoint, null); + /** + * Deserialize this transaction input from a given payload. + * + * @param payload payload to deserialize from + * @param parentTransaction parent transaction of the input + * @return read message + * @throws BufferUnderflowException if the read message extends beyond the remaining bytes of the payload + */ + public static TransactionInput read(ByteBuffer payload, Transaction parentTransaction) throws BufferUnderflowException, ProtocolException { + Objects.requireNonNull(parentTransaction); + TransactionOutPoint outpoint = TransactionOutPoint.read(payload); + byte[] scriptBytes = Buffers.readLengthPrefixedBytes(payload); + long sequence = ByteUtils.readUint32(payload); + return new TransactionInput(parentTransaction, scriptBytes, outpoint, sequence, null); } public TransactionInput(@Nullable Transaction parentTransaction, byte[] scriptBytes, - TransactionOutPoint outpoint, @Nullable Coin value) { - super(); + TransactionOutPoint outpoint) { + this(parentTransaction, scriptBytes, outpoint, NO_SEQUENCE, null); + } + + public TransactionInput(@Nullable Transaction parentTransaction, byte[] scriptBytes, + TransactionOutPoint outpoint, @Nullable Coin value) { + this(parentTransaction, scriptBytes, outpoint, NO_SEQUENCE, value); + } + + private TransactionInput(@Nullable Transaction parentTransaction, byte[] scriptBytes, + TransactionOutPoint outpoint, long sequence, @Nullable Coin value) { this.scriptBytes = scriptBytes; this.outpoint = outpoint; - this.sequence = NO_SEQUENCE; + this.sequence = sequence; this.value = value; setParent(parentTransaction); } @@ -135,17 +154,6 @@ public class TransactionInput extends Message { this.value = output.getValue(); } - /** - * Deserializes an input message. This is usually part of a transaction message. - * @param payload Bitcoin protocol formatted byte array containing message content. - * @throws ProtocolException - */ - public TransactionInput(@Nullable Transaction parentTransaction, ByteBuffer payload) throws ProtocolException { - super(payload); - setParent(parentTransaction); - this.value = null; - } - /** * Gets the index of this input in the parent transaction, or throws if this input is freestanding. Iterates * over the parents list to discover this. @@ -157,14 +165,41 @@ public class TransactionInput extends Message { return myIndex; } - @Override - protected void parse(ByteBuffer payload) throws BufferUnderflowException, ProtocolException { - outpoint = TransactionOutPoint.read(payload); - scriptBytes = Buffers.readLengthPrefixedBytes(payload); - sequence = ByteUtils.readUint32(payload); + /** + * Write this transaction input into the given buffer. + * + * @param buf buffer to write into + * @return the buffer + * @throws BufferOverflowException if the input doesn't fit the remaining buffer + */ + public ByteBuffer write(ByteBuffer buf) throws BufferOverflowException { + buf.put(outpoint.serialize()); + Buffers.writeLengthPrefixedBytes(buf, scriptBytes); + ByteUtils.writeInt32LE(sequence, buf); + return buf; } - @Override + /** + * Allocates a byte array and writes this transaction input into it. + * + * @return byte array containing the transaction input + */ + public byte[] serialize() { + return write(ByteBuffer.allocate(getMessageSize())).array(); + } + + /** @deprecated use {@link #serialize()} */ + @Deprecated + public byte[] bitcoinSerialize() { + return serialize(); + } + + /** + * Return the size of the serialized message. Note that if the message was deserialized from a payload, this + * size can differ from the size of the original payload. + * + * @return size of the serialized message in bytes + */ public int getMessageSize() { int size = TransactionOutPoint.BYTES; size += VarInt.sizeOf(scriptBytes.length) + scriptBytes.length; @@ -172,14 +207,6 @@ public class TransactionInput extends Message { return size; } - @Override - protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { - stream.write(outpoint.serialize()); - stream.write(VarInt.of(scriptBytes.length).serialize()); - stream.write(scriptBytes); - ByteUtils.writeInt32LE(sequence, stream); - } - /** * Coinbase transactions have special inputs with hashes of zero. If this is such an input, returns true. */ diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index f51c7e80d..0dafb52b7 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -5372,7 +5372,7 @@ public class Wallet extends BaseTaggableObject private void addSuppliedInputs(Transaction tx, List