TransactionWitness: make immutable

Except for the pushes themselves.
This commit is contained in:
Andreas Schildbach 2023-04-04 17:57:56 +02:00
parent 2699ea1708
commit ae22162de5
4 changed files with 32 additions and 36 deletions

View File

@ -28,12 +28,14 @@ import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static org.bitcoinj.base.internal.Preconditions.checkArgument;
public class TransactionWitness {
public static final TransactionWitness EMPTY = new TransactionWitness(0);
public static final TransactionWitness EMPTY = TransactionWitness.of(Collections.emptyList());
/**
* Creates the stack pushes necessary to redeem a P2WPKH output. If given signature is null, an empty push will be
@ -42,24 +44,22 @@ public class TransactionWitness {
public static TransactionWitness redeemP2WPKH(@Nullable TransactionSignature signature, ECKey pubKey) {
checkArgument(pubKey.isCompressed(), () ->
"only compressed keys allowed");
TransactionWitness witness = new TransactionWitness(2);
witness.setPush(0, signature != null ? signature.encodeToBitcoin() : new byte[0]); // signature
witness.setPush(1, pubKey.getPubKey()); // pubkey
return witness;
List<byte[]> pushes = new ArrayList<>(2);
pushes.add(signature != null ? signature.encodeToBitcoin() : new byte[0]); // signature
pushes.add(pubKey.getPubKey()); // pubkey
return TransactionWitness.of(pushes);
}
/**
* Creates the stack pushes necessary to redeem a P2WSH output.
*/
public static TransactionWitness redeemP2WSH(Script witnessScript, TransactionSignature... signatures) {
TransactionWitness witness = new TransactionWitness(signatures.length + 2);
witness.setPush(0, new byte[]{});
int i;
for (i = 0; i < signatures.length; i++) {
witness.setPush(i + 1, signatures[i].encodeToBitcoin());
}
witness.setPush(i + 1, witnessScript.getProgram());
return witness;
List<byte[]> pushes = new ArrayList<>(signatures.length + 2);
pushes.add(new byte[] {});
for (TransactionSignature signature : signatures)
pushes.add(signature.encodeToBitcoin());
pushes.add(witnessScript.getProgram());
return TransactionWitness.of(pushes);
}
/**
@ -72,6 +72,16 @@ public class TransactionWitness {
return new TransactionWitness(pushes);
}
/**
* Construct a transaction witness from a given list of arbitrary pushes.
*
* @param pushes list of pushes
* @return constructed transaction witness
*/
public static TransactionWitness of(byte[]... pushes) {
return of(Arrays.asList(pushes));
}
/**
* Deserialize this transaction witness from a given payload.
*
@ -89,10 +99,6 @@ public class TransactionWitness {
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;
}
@ -105,13 +111,6 @@ public class TransactionWitness {
return pushes.size();
}
public void setPush(int i, byte[] value) {
while (i >= pushes.size()) {
pushes.add(new byte[]{});
}
pushes.set(i, value);
}
/**
* Write this transaction witness into the given buffer.
*

View File

@ -656,10 +656,10 @@ public class WalletProtobufSerializer {
if (inputProto.hasWitness()) {
Protos.ScriptWitness witnessProto = inputProto.getWitness();
if (witnessProto.getDataCount() > 0) {
TransactionWitness witness = new TransactionWitness(witnessProto.getDataCount());
List<byte[]> pushes = new ArrayList<>(witnessProto.getDataCount());
for (int j = 0; j < witnessProto.getDataCount(); j++)
witness.setPush(j, witnessProto.getData(j).toByteArray());
input.setWitness(witness);
pushes.add(witnessProto.getData(j).toByteArray());
input.setWitness(TransactionWitness.of(pushes));
}
}
tx.addInput(input);

View File

@ -713,8 +713,7 @@ public class TransactionTest {
inputTx.addOutput(Coin.FIFTY_COINS, new ECKey());
this.addInput(inputTx.getOutput(0));
this.getInput(0).disconnect();
TransactionWitness witness = new TransactionWitness(1);
witness.setPush(0, new byte[] {0});
TransactionWitness witness = TransactionWitness.of(new byte[] { 0 });
this.getInput(0).setWitness(witness);
this.addOutput(Coin.COIN, new ECKey());

View File

@ -40,16 +40,14 @@ public class TransactionWitnessTest {
@Test
public void testToString() {
TransactionWitness w1 = new TransactionWitness(0);
TransactionWitness w1 = TransactionWitness.EMPTY;
assertEquals("", w1.toString());
TransactionWitness w2 = new TransactionWitness(2);
assertEquals("", w2.toString());
TransactionWitness w2 = TransactionWitness.of(new byte[0], new byte[0]);
assertEquals("EMPTY EMPTY", w2.toString());
TransactionWitness w3 = new TransactionWitness(3);
w3.setPush(0, ByteUtils.parseHex("123aaa"));
w3.setPush(1, ByteUtils.parseHex("123bbb"));
w3.setPush(3, ByteUtils.parseHex("123ccc"));
TransactionWitness w3 = TransactionWitness.of(ByteUtils.parseHex("123aaa"), ByteUtils.parseHex("123bbb"),
new byte[0], ByteUtils.parseHex("123ccc"));
assertEquals("123aaa 123bbb EMPTY 123ccc", w3.toString());
}