mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-18 21:32:35 +01:00
TransactionWitness: add read and write helpers
* A static constructur `read()` deserializes from a payload. * A `write()` helper replaces `bitcoinSerializeToStream()`. * A `serialize()` helper gets the serialized bytes. This comes with a test.
This commit is contained in:
parent
41f684523d
commit
2699ea1708
@ -706,16 +706,8 @@ public class Transaction extends Message {
|
||||
|
||||
private void parseWitnesses(ByteBuffer payload) throws BufferUnderflowException, ProtocolException {
|
||||
int numWitnesses = inputs.size();
|
||||
for (int i = 0; i < numWitnesses; i++) {
|
||||
VarInt pushCountVarInt = VarInt.read(payload);
|
||||
int pushCount = pushCountVarInt.intValue();
|
||||
TransactionWitness witness = new TransactionWitness(pushCount);
|
||||
getInput(i).setWitness(witness);
|
||||
for (int y = 0; y < pushCount; y++) {
|
||||
byte[] push = Buffers.readLengthPrefixedBytes(payload);
|
||||
witness.setPush(y, push);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < numWitnesses; i++)
|
||||
getInput(i).setWitness(TransactionWitness.read(payload));
|
||||
}
|
||||
|
||||
/** @return true of the transaction has any witnesses in any of its inputs */
|
||||
@ -1532,9 +1524,8 @@ public class Transaction extends Message {
|
||||
out.bitcoinSerializeToStream(stream);
|
||||
// script_witnisses
|
||||
if (useSegwit) {
|
||||
for (TransactionInput in : inputs) {
|
||||
in.getWitness().bitcoinSerializeToStream(stream);
|
||||
}
|
||||
for (TransactionInput in : inputs)
|
||||
stream.write(in.getWitness().serialize());
|
||||
}
|
||||
// lock_time
|
||||
writeInt32LE(vLockTime.rawValue(), stream);
|
||||
|
@ -15,6 +15,7 @@
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.base.VarInt;
|
||||
import org.bitcoinj.base.internal.Buffers;
|
||||
import org.bitcoinj.base.internal.ByteUtils;
|
||||
import org.bitcoinj.base.internal.InternalUtils;
|
||||
import org.bitcoinj.crypto.ECKey;
|
||||
@ -22,8 +23,9 @@ import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -60,12 +62,41 @@ public class TransactionWitness {
|
||||
return witness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a transaction witness from a given list of arbitrary pushes.
|
||||
*
|
||||
* @param pushes list of pushes
|
||||
* @return constructed transaction witness
|
||||
*/
|
||||
public static TransactionWitness of(List<byte[]> pushes) {
|
||||
return new TransactionWitness(pushes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize this transaction witness from a given payload.
|
||||
*
|
||||
* @param payload payload to deserialize from
|
||||
* @return read message
|
||||
* @throws BufferUnderflowException if the read message extends beyond the remaining bytes of the payload
|
||||
*/
|
||||
public static TransactionWitness read(ByteBuffer payload) throws BufferUnderflowException {
|
||||
int pushCount = VarInt.read(payload).intValue();
|
||||
List<byte[]> pushes = new ArrayList<>(Math.min(pushCount, Utils.MAX_INITIAL_ARRAY_LENGTH));
|
||||
for (int y = 0; y < pushCount; y++)
|
||||
pushes.add(Buffers.readLengthPrefixedBytes(payload));
|
||||
return new TransactionWitness(pushes);
|
||||
}
|
||||
|
||||
private final List<byte[]> pushes;
|
||||
|
||||
public TransactionWitness(int pushCount) {
|
||||
pushes = new ArrayList<>(Math.min(pushCount, Utils.MAX_INITIAL_ARRAY_LENGTH));
|
||||
}
|
||||
|
||||
private TransactionWitness(List<byte[]> pushes) {
|
||||
this.pushes = pushes;
|
||||
}
|
||||
|
||||
public byte[] getPush(int i) {
|
||||
return pushes.get(i);
|
||||
}
|
||||
@ -81,21 +112,42 @@ public class TransactionWitness {
|
||||
pushes.set(i, value);
|
||||
}
|
||||
|
||||
protected int getMessageSize() {
|
||||
/**
|
||||
* Write this transaction witness into the given buffer.
|
||||
*
|
||||
* @param buf buffer to write into
|
||||
* @return the buffer
|
||||
* @throws BufferOverflowException if the serialized data doesn't fit the remaining buffer
|
||||
*/
|
||||
public ByteBuffer write(ByteBuffer buf) throws BufferOverflowException {
|
||||
VarInt.of(pushes.size()).write(buf);
|
||||
for (byte[] push : pushes)
|
||||
Buffers.writeLengthPrefixedBytes(buf, push);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a byte array and writes this transaction witness into it.
|
||||
*
|
||||
* @return byte array containing the transaction witness
|
||||
*/
|
||||
public byte[] serialize() {
|
||||
return write(ByteBuffer.allocate(getMessageSize())).array();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = VarInt.sizeOf(pushes.size());
|
||||
for (byte[] push : pushes)
|
||||
size += VarInt.sizeOf(push.length) + push.length;
|
||||
return size;
|
||||
}
|
||||
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(VarInt.of(pushes.size()).serialize());
|
||||
for (byte[] push : pushes) {
|
||||
stream.write(VarInt.of(push.length).serialize());
|
||||
stream.write(push);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> stringPushes = new ArrayList<>(pushes.size());
|
||||
|
@ -754,7 +754,7 @@ public class TransactionTest {
|
||||
stream.write(push);
|
||||
}
|
||||
|
||||
in.getWitness().bitcoinSerializeToStream(stream);
|
||||
stream.write(in.getWitness().serialize());
|
||||
}
|
||||
}
|
||||
// lock_time
|
||||
|
@ -14,16 +14,28 @@
|
||||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import junitparams.JUnitParamsRunner;
|
||||
import junitparams.Parameters;
|
||||
import org.bitcoinj.base.internal.ByteUtils;
|
||||
import org.bitcoinj.crypto.ECKey;
|
||||
import org.bitcoinj.crypto.SignatureDecodeException;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.script.Script;
|
||||
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.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
@RunWith(JUnitParamsRunner.class)
|
||||
public class TransactionWitnessTest {
|
||||
|
||||
@Test
|
||||
@ -57,4 +69,27 @@ public class TransactionWitnessTest {
|
||||
assertArrayEquals(signature2.encodeToBitcoin(), witness.getPush(2));
|
||||
assertArrayEquals(witnessScript.getProgram(), witness.getPush(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameters(method = "randomWitness")
|
||||
public void readAndWrite(TransactionWitness witness) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(witness.getMessageSize());
|
||||
witness.write(buf);
|
||||
assertFalse(buf.hasRemaining());
|
||||
((Buffer) buf).rewind();
|
||||
TransactionWitness witnessCopy = TransactionWitness.read(buf);
|
||||
assertFalse(buf.hasRemaining());
|
||||
assertEquals(witness, witnessCopy);
|
||||
}
|
||||
|
||||
private Iterator<TransactionWitness> randomWitness() {
|
||||
Random random = new Random();
|
||||
return Stream.generate(() -> {
|
||||
return TransactionWitness.of(Stream.generate(() -> {
|
||||
byte[] randomBytes = new byte[random.nextInt(50)];
|
||||
random.nextBytes(randomBytes);
|
||||
return randomBytes;
|
||||
}).limit(random.nextInt(10)).collect(Collectors.toList()));
|
||||
}).limit(10).iterator();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user