diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java
index 57570e5d..adbcfcfa 100644
--- a/core/src/main/java/org/bitcoinj/core/Transaction.java
+++ b/core/src/main/java/org/bitcoinj/core/Transaction.java
@@ -460,16 +460,37 @@ public class Transaction extends ChildMessage {
/**
* These constants are a part of a scriptSig signature on the inputs. They define the details of how a
* transaction can be redeemed, specifically, they control how the hash of the transaction is calculated.
- *
- * In Bitcoin Core, this enum also has another flag, SIGHASH_ANYONECANPAY. In this implementation,
- * that's kept separate. Only SIGHASH_ALL is actually used in Bitcoin Core today. The other flags
- * exist to allow for distributed contracts.
*/
public enum SigHash {
- ALL, // 1
- NONE, // 2
- SINGLE, // 3
+ ALL(1),
+ NONE(2),
+ SINGLE(3),
+ ANYONECANPAY(0x80), // Caution: Using this type in isolation is non-standard. Treated similar to ANYONECANPAY_ALL.
+ ANYONECANPAY_ALL(0x81),
+ ANYONECANPAY_NONE(0x82),
+ ANYONECANPAY_SINGLE(0x83),
+ UNSET(0); // Caution: Using this type in isolation is non-standard. Treated similar to ALL.
+
+ public final int value;
+
+ /**
+ * @param value
+ */
+ private SigHash(final int value) {
+ this.value = value;
+ }
+
+ /**
+ * @return the value as a byte
+ */
+ public byte byteValue() {
+ return (byte) this.value;
+ }
}
+
+ /**
+ * @deprecated Instead use SigHash.ANYONECANPAY.value or SigHash.ANYONECANPAY.byteValue() as appropriate.
+ */
public static final byte SIGHASH_ANYONECANPAY_VALUE = (byte) 0x80;
@Override
@@ -958,14 +979,14 @@ public class Transaction extends ChildMessage {
TransactionInput input = tx.inputs.get(inputIndex);
input.setScriptBytes(connectedScript);
- if ((sigHashType & 0x1f) == (SigHash.NONE.ordinal() + 1)) {
+ if ((sigHashType & 0x1f) == SigHash.NONE.value) {
// SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a "blank cheque".
tx.outputs = new ArrayList(0);
// The signature isn't broken by new versions of the transaction issued by other parties.
for (int i = 0; i < tx.inputs.size(); i++)
if (i != inputIndex)
tx.inputs.get(i).setSequenceNumber(0);
- } else if ((sigHashType & 0x1f) == (SigHash.SINGLE.ordinal() + 1)) {
+ } else if ((sigHashType & 0x1f) == SigHash.SINGLE.value) {
// SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output).
if (inputIndex >= tx.outputs.size()) {
// The input index is beyond the number of outputs, it's a buggy signature made by a broken
@@ -989,7 +1010,7 @@ public class Transaction extends ChildMessage {
tx.inputs.get(i).setSequenceNumber(0);
}
- if ((sigHashType & SIGHASH_ANYONECANPAY_VALUE) == SIGHASH_ANYONECANPAY_VALUE) {
+ if ((sigHashType & SigHash.ANYONECANPAY.value) == SigHash.ANYONECANPAY.value) {
// SIGHASH_ANYONECANPAY means the signature in the input is not broken by changes/additions/removals
// of other inputs. For example, this is useful for building assurance contracts.
tx.inputs = new ArrayList();
diff --git a/core/src/main/java/org/bitcoinj/crypto/TransactionSignature.java b/core/src/main/java/org/bitcoinj/crypto/TransactionSignature.java
index cbff1ff3..545804a1 100644
--- a/core/src/main/java/org/bitcoinj/crypto/TransactionSignature.java
+++ b/core/src/main/java/org/bitcoinj/crypto/TransactionSignature.java
@@ -19,7 +19,8 @@ package org.bitcoinj.crypto;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.VerificationException;
-
+import org.bitcoinj.core.Transaction.SigHash;
+import com.google.common.base.Preconditions;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
@@ -39,7 +40,7 @@ public class TransactionSignature extends ECKey.ECDSASignature {
/** Constructs a signature with the given components and SIGHASH_ALL. */
public TransactionSignature(BigInteger r, BigInteger s) {
- this(r, s, Transaction.SigHash.ALL.ordinal() + 1);
+ this(r, s, Transaction.SigHash.ALL.value);
}
/** Constructs a signature with the given components and raw sighash flag bytes (needed for rule compatibility). */
@@ -67,9 +68,10 @@ public class TransactionSignature extends ECKey.ECDSASignature {
/** Calculates the byte used in the protocol to represent the combination of mode and anyoneCanPay. */
public static int calcSigHashValue(Transaction.SigHash mode, boolean anyoneCanPay) {
- int sighashFlags = mode.ordinal() + 1;
+ Preconditions.checkArgument(SigHash.ALL == mode || SigHash.NONE == mode || SigHash.SINGLE == mode); // enforce compatibility since this code was made before the SigHash enum was updated
+ int sighashFlags = mode.value;
if (anyoneCanPay)
- sighashFlags |= Transaction.SIGHASH_ANYONECANPAY_VALUE;
+ sighashFlags |= Transaction.SigHash.ANYONECANPAY.value;
return sighashFlags;
}
@@ -90,8 +92,8 @@ public class TransactionSignature extends ECKey.ECDSASignature {
if (signature.length < 9 || signature.length > 73)
return false;
- int hashType = signature[signature.length-1] & ~Transaction.SIGHASH_ANYONECANPAY_VALUE;
- if (hashType < (Transaction.SigHash.ALL.ordinal() + 1) || hashType > (Transaction.SigHash.SINGLE.ordinal() + 1))
+ int hashType = (signature[signature.length-1] & 0xff) & ~Transaction.SigHash.ANYONECANPAY.value; // mask the byte to prevent sign-extension hurting us
+ if (hashType < Transaction.SigHash.ALL.value || hashType > Transaction.SigHash.SINGLE.value)
return false;
// "wrong type" "wrong length marker"
@@ -121,14 +123,14 @@ public class TransactionSignature extends ECKey.ECDSASignature {
}
public boolean anyoneCanPay() {
- return (sighashFlags & Transaction.SIGHASH_ANYONECANPAY_VALUE) != 0;
+ return (sighashFlags & Transaction.SigHash.ANYONECANPAY.value) != 0;
}
public Transaction.SigHash sigHashMode() {
final int mode = sighashFlags & 0x1f;
- if (mode == Transaction.SigHash.NONE.ordinal() + 1)
+ if (mode == Transaction.SigHash.NONE.value)
return Transaction.SigHash.NONE;
- else if (mode == Transaction.SigHash.SINGLE.ordinal() + 1)
+ else if (mode == Transaction.SigHash.SINGLE.value)
return Transaction.SigHash.SINGLE;
else
return Transaction.SigHash.ALL;
diff --git a/core/src/test/java/org/bitcoinj/core/ECKeyTest.java b/core/src/test/java/org/bitcoinj/core/ECKeyTest.java
index c71df308..1792da20 100644
--- a/core/src/test/java/org/bitcoinj/core/ECKeyTest.java
+++ b/core/src/test/java/org/bitcoinj/core/ECKeyTest.java
@@ -447,7 +447,7 @@ public class ECKeyTest {
new Random().nextBytes(hash);
byte[] sigBytes = key.sign(Sha256Hash.wrap(hash)).encodeToDER();
byte[] encodedSig = Arrays.copyOf(sigBytes, sigBytes.length + 1);
- encodedSig[sigBytes.length] = (byte) (Transaction.SigHash.ALL.ordinal() + 1);
+ encodedSig[sigBytes.length] = Transaction.SigHash.ALL.byteValue();
if (!TransactionSignature.isEncodingCanonical(encodedSig)) {
log.error(Utils.HEX.encode(sigBytes));
fail();
diff --git a/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java b/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java
index 2725438b..742bee75 100644
--- a/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java
+++ b/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java
@@ -759,7 +759,7 @@ public class FullBlockTestGenerator {
try {
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(73);
bos.write(coinbaseOutKey.sign(hash).encodeToDER());
- bos.write(SigHash.SINGLE.ordinal() + 1);
+ bos.write(SigHash.SINGLE.value);
byte[] signature = bos.toByteArray();
ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(signature.length + b39p2shScriptPubKey.length + 3);
@@ -830,7 +830,7 @@ public class FullBlockTestGenerator {
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(
73);
bos.write(coinbaseOutKey.sign(hash).encodeToDER());
- bos.write(SigHash.SINGLE.ordinal() + 1);
+ bos.write(SigHash.SINGLE.value);
byte[] signature = bos.toByteArray();
ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(
diff --git a/core/src/test/java/org/bitcoinj/core/TransactionTest.java b/core/src/test/java/org/bitcoinj/core/TransactionTest.java
index 16597f39..34e9b27f 100644
--- a/core/src/test/java/org/bitcoinj/core/TransactionTest.java
+++ b/core/src/test/java/org/bitcoinj/core/TransactionTest.java
@@ -392,14 +392,14 @@ public class TransactionTest {
final Transaction tx = block1.getTransactions().get(1);
final String txHash = tx.getHashAsString();
- final String txNormalizedHash = tx.hashForSignature(0, new byte[0], (byte) (Transaction.SigHash.ALL.ordinal() + 1)).toString();
+ final String txNormalizedHash = tx.hashForSignature(0, new byte[0], Transaction.SigHash.ALL.byteValue()).toString();
for (int i = 0; i < 100; i++) {
// ensure the transaction object itself was not modified; if it was, the hash will change
assertEquals(txHash, tx.getHashAsString());
new Thread(){
public void run() {
- assertEquals(txNormalizedHash, tx.hashForSignature(0, new byte[0], (byte) (Transaction.SigHash.ALL.ordinal() + 1)).toString());
+ assertEquals(txNormalizedHash, tx.hashForSignature(0, new byte[0], Transaction.SigHash.ALL.byteValue()).toString());
}
};
}
diff --git a/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java b/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java
index 8565b6df..7b8cfb69 100644
--- a/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java
+++ b/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java
@@ -513,7 +513,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
byte[] refundSigCopy = Arrays.copyOf(refundSig, refundSig.length);
- refundSigCopy[refundSigCopy.length - 1] = (byte) (Transaction.SigHash.NONE.ordinal() + 1);
+ refundSigCopy[refundSigCopy.length - 1] = Transaction.SigHash.NONE.byteValue();
try {
clientV1State().provideRefundSignature(refundSigCopy, null);
fail();
@@ -627,7 +627,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
totalPayment = totalPayment.add(size);
byte[] signatureCopy = Arrays.copyOf(signature, signature.length);
- signatureCopy[signatureCopy.length - 1] = (byte) ((Transaction.SigHash.NONE.ordinal() + 1) | 0x80);
+ signatureCopy[signatureCopy.length - 1] = Transaction.SigHash.ANYONECANPAY_NONE.byteValue();
try {
serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy);
fail();
@@ -659,7 +659,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
assertEquals(totalPayment, halfCoin);
signatureCopy = Arrays.copyOf(signature, signature.length);
- signatureCopy[signatureCopy.length - 1] = (byte) ((Transaction.SigHash.SINGLE.ordinal() + 1) | 0x80);
+ signatureCopy[signatureCopy.length - 1] = Transaction.SigHash.ANYONECANPAY_SINGLE.byteValue();
try {
serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy);
fail();