mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-31 20:11:23 +00:00
Complete the SigHash enum and make updates to stop using the ordinal while preserving the ordinal for any existing code that might use it.
This commit is contained in:
committed by
Andreas Schildbach
parent
c02c5ff249
commit
2748b35181
@@ -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.
|
||||
* <p/>
|
||||
* 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<TransactionOutput>(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<TransactionInput>();
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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(
|
||||
|
@@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user