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();