diff --git a/core/src/main/java/org/bitcoinj/core/BloomFilter.java b/core/src/main/java/org/bitcoinj/core/BloomFilter.java index d5b448be9..44b657d7a 100644 --- a/core/src/main/java/org/bitcoinj/core/BloomFilter.java +++ b/core/src/main/java/org/bitcoinj/core/BloomFilter.java @@ -257,7 +257,7 @@ public class BloomFilter extends Message { /** Inserts the given transaction outpoint. */ public synchronized void insert(TransactionOutPoint outpoint) { - insert(outpoint.bitcoinSerialize()); + insert(outpoint.serialize()); } /** @@ -359,7 +359,7 @@ public class BloomFilter extends Message { } if (found) return true; for (TransactionInput input : tx.getInputs()) { - if (contains(input.getOutpoint().bitcoinSerialize())) { + if (contains(input.getOutpoint().serialize())) { return true; } for (ScriptChunk chunk : input.getScriptSig().getChunks()) { diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index 1b815ded4..731112f13 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -682,7 +682,7 @@ public class Transaction extends Message { TransactionInput input = new TransactionInput(this, payload.slice()); inputs.add(input); // intentionally read again, due to the slice above - Buffers.skipBytes(payload, TransactionOutPoint.MESSAGE_LENGTH); + Buffers.skipBytes(payload, TransactionOutPoint.BYTES); VarInt scriptLenVarInt = VarInt.read(payload); int scriptLen = scriptLenVarInt.intValue(); Buffers.skipBytes(payload, scriptLen + 4); diff --git a/core/src/main/java/org/bitcoinj/core/TransactionInput.java b/core/src/main/java/org/bitcoinj/core/TransactionInput.java index 5a077f45a..4fd9d219b 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionInput.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionInput.java @@ -159,14 +159,14 @@ public class TransactionInput extends Message { @Override protected void parse(ByteBuffer payload) throws BufferUnderflowException, ProtocolException { - outpoint = new TransactionOutPoint(payload); + outpoint = TransactionOutPoint.read(payload); scriptBytes = Buffers.readLengthPrefixedBytes(payload); sequence = ByteUtils.readUint32(payload); } @Override public int getMessageSize() { - int size = outpoint.getMessageSize(); + int size = TransactionOutPoint.BYTES; size += VarInt.sizeOf(scriptBytes.length) + scriptBytes.length; size += 4; // sequence return size; @@ -174,7 +174,7 @@ public class TransactionInput extends Message { @Override protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { - outpoint.bitcoinSerializeToStream(stream); + stream.write(outpoint.serialize()); stream.write(VarInt.of(scriptBytes.length).serialize()); stream.write(scriptBytes); ByteUtils.writeInt32LE(sequence, stream); diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java b/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java index 8e40ba8c2..e380a7a28 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java @@ -29,8 +29,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.nio.Buffer; +import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.Objects; @@ -43,9 +43,8 @@ import static org.bitcoinj.base.internal.Preconditions.checkState; * *
Instances of this class are not safe for use by multiple threads.
*/ -public class TransactionOutPoint extends Message { - - static final int MESSAGE_LENGTH = 36; +public class TransactionOutPoint { + public static final int BYTES = 36; /** Special outpoint that normally marks a coinbase input. It's also used as a test dummy. */ public static final TransactionOutPoint UNCONNECTED = @@ -62,6 +61,19 @@ public class TransactionOutPoint extends Message { // The connected output. TransactionOutput connectedOutput; + /** + * Deserialize this transaction outpoint from a given payload. + * + * @param payload payload to deserialize from + * @return read transaction outpoint + * @throws BufferUnderflowException if the read message extends beyond the remaining bytes of the payload + */ + public static TransactionOutPoint read(ByteBuffer payload) throws BufferUnderflowException, ProtocolException { + Sha256Hash hash = Sha256Hash.read(payload); + long index = ByteUtils.readUint32(payload); + return new TransactionOutPoint(index, hash); + } + public TransactionOutPoint(long index, Transaction fromTx) { super(); checkArgument(index >= 0 && index <= ByteUtils.MAX_UNSIGNED_INTEGER, () -> @@ -85,28 +97,37 @@ public class TransactionOutPoint extends Message { } /** - * Deserializes the message. This is usually part of a transaction message. - * @throws ProtocolException + * Write this transaction outpoint into the given buffer. + * + * @param buf buffer to write into + * @return the buffer + * @throws BufferOverflowException if the outpoint doesn't fit the remaining buffer */ - public TransactionOutPoint(ByteBuffer payload) throws ProtocolException { - super(payload); + public ByteBuffer write(ByteBuffer buf) throws BufferOverflowException { + buf.put(hash.serialize()); + ByteUtils.writeInt32LE(index, buf); + return buf; } - @Override - protected void parse(ByteBuffer payload) throws BufferUnderflowException, ProtocolException { - hash = Sha256Hash.read(payload); - index = ByteUtils.readUint32(payload); + /** + * Allocates a byte array and writes this transaction outpoint into it. + * + * @return byte array containing the transaction outpoint + */ + public byte[] serialize() { + return write(ByteBuffer.allocate(BYTES)).array(); } - @Override + /** @deprecated use {@link #serialize()} */ + @Deprecated + public byte[] bitcoinSerialize() { + return serialize(); + } + + /** @deprecated use {@link #BYTES} */ + @Deprecated public int getMessageSize() { - return MESSAGE_LENGTH; - } - - @Override - protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { - stream.write(hash.serialize()); - ByteUtils.writeInt32LE(index, stream); + return BYTES; } /** diff --git a/core/src/test/java/org/bitcoinj/core/TransactionOutPointTest.java b/core/src/test/java/org/bitcoinj/core/TransactionOutPointTest.java new file mode 100644 index 000000000..92c9e12c2 --- /dev/null +++ b/core/src/test/java/org/bitcoinj/core/TransactionOutPointTest.java @@ -0,0 +1,56 @@ +/* + * Copyright by the original author or authors. + * + * 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 junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import org.bitcoinj.base.Sha256Hash; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Random; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +@RunWith(JUnitParamsRunner.class) +public class TransactionOutPointTest { + @Test + @Parameters(method = "randomOutPoints") + public void readAndWrite(TransactionOutPoint outpoint) { + ByteBuffer buf = ByteBuffer.allocate(TransactionOutPoint.BYTES); + outpoint.write(buf); + assertFalse(buf.hasRemaining()); + ((Buffer) buf).rewind(); + TransactionOutPoint outpointCopy = TransactionOutPoint.read(buf); + assertFalse(buf.hasRemaining()); + assertEquals(outpoint, outpointCopy); + } + + private Iterator