mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 05:27:17 +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
|
* 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.
|
* 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 {
|
public enum SigHash {
|
||||||
ALL, // 1
|
ALL(1),
|
||||||
NONE, // 2
|
NONE(2),
|
||||||
SINGLE, // 3
|
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;
|
public static final byte SIGHASH_ANYONECANPAY_VALUE = (byte) 0x80;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -958,14 +979,14 @@ public class Transaction extends ChildMessage {
|
|||||||
TransactionInput input = tx.inputs.get(inputIndex);
|
TransactionInput input = tx.inputs.get(inputIndex);
|
||||||
input.setScriptBytes(connectedScript);
|
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".
|
// SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a "blank cheque".
|
||||||
tx.outputs = new ArrayList<TransactionOutput>(0);
|
tx.outputs = new ArrayList<TransactionOutput>(0);
|
||||||
// The signature isn't broken by new versions of the transaction issued by other parties.
|
// The signature isn't broken by new versions of the transaction issued by other parties.
|
||||||
for (int i = 0; i < tx.inputs.size(); i++)
|
for (int i = 0; i < tx.inputs.size(); i++)
|
||||||
if (i != inputIndex)
|
if (i != inputIndex)
|
||||||
tx.inputs.get(i).setSequenceNumber(0);
|
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).
|
// SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output).
|
||||||
if (inputIndex >= tx.outputs.size()) {
|
if (inputIndex >= tx.outputs.size()) {
|
||||||
// The input index is beyond the number of outputs, it's a buggy signature made by a broken
|
// 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);
|
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
|
// 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.
|
// of other inputs. For example, this is useful for building assurance contracts.
|
||||||
tx.inputs = new ArrayList<TransactionInput>();
|
tx.inputs = new ArrayList<TransactionInput>();
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ package org.bitcoinj.crypto;
|
|||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.VerificationException;
|
import org.bitcoinj.core.VerificationException;
|
||||||
|
import org.bitcoinj.core.Transaction.SigHash;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
@@ -39,7 +40,7 @@ public class TransactionSignature extends ECKey.ECDSASignature {
|
|||||||
|
|
||||||
/** Constructs a signature with the given components and SIGHASH_ALL. */
|
/** Constructs a signature with the given components and SIGHASH_ALL. */
|
||||||
public TransactionSignature(BigInteger r, BigInteger s) {
|
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). */
|
/** 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. */
|
/** Calculates the byte used in the protocol to represent the combination of mode and anyoneCanPay. */
|
||||||
public static int calcSigHashValue(Transaction.SigHash mode, boolean 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)
|
if (anyoneCanPay)
|
||||||
sighashFlags |= Transaction.SIGHASH_ANYONECANPAY_VALUE;
|
sighashFlags |= Transaction.SigHash.ANYONECANPAY.value;
|
||||||
return sighashFlags;
|
return sighashFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +92,8 @@ public class TransactionSignature extends ECKey.ECDSASignature {
|
|||||||
if (signature.length < 9 || signature.length > 73)
|
if (signature.length < 9 || signature.length > 73)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int hashType = signature[signature.length-1] & ~Transaction.SIGHASH_ANYONECANPAY_VALUE;
|
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.ordinal() + 1) || hashType > (Transaction.SigHash.SINGLE.ordinal() + 1))
|
if (hashType < Transaction.SigHash.ALL.value || hashType > Transaction.SigHash.SINGLE.value)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// "wrong type" "wrong length marker"
|
// "wrong type" "wrong length marker"
|
||||||
@@ -121,14 +123,14 @@ public class TransactionSignature extends ECKey.ECDSASignature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean anyoneCanPay() {
|
public boolean anyoneCanPay() {
|
||||||
return (sighashFlags & Transaction.SIGHASH_ANYONECANPAY_VALUE) != 0;
|
return (sighashFlags & Transaction.SigHash.ANYONECANPAY.value) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transaction.SigHash sigHashMode() {
|
public Transaction.SigHash sigHashMode() {
|
||||||
final int mode = sighashFlags & 0x1f;
|
final int mode = sighashFlags & 0x1f;
|
||||||
if (mode == Transaction.SigHash.NONE.ordinal() + 1)
|
if (mode == Transaction.SigHash.NONE.value)
|
||||||
return Transaction.SigHash.NONE;
|
return Transaction.SigHash.NONE;
|
||||||
else if (mode == Transaction.SigHash.SINGLE.ordinal() + 1)
|
else if (mode == Transaction.SigHash.SINGLE.value)
|
||||||
return Transaction.SigHash.SINGLE;
|
return Transaction.SigHash.SINGLE;
|
||||||
else
|
else
|
||||||
return Transaction.SigHash.ALL;
|
return Transaction.SigHash.ALL;
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ public class ECKeyTest {
|
|||||||
new Random().nextBytes(hash);
|
new Random().nextBytes(hash);
|
||||||
byte[] sigBytes = key.sign(Sha256Hash.wrap(hash)).encodeToDER();
|
byte[] sigBytes = key.sign(Sha256Hash.wrap(hash)).encodeToDER();
|
||||||
byte[] encodedSig = Arrays.copyOf(sigBytes, sigBytes.length + 1);
|
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)) {
|
if (!TransactionSignature.isEncodingCanonical(encodedSig)) {
|
||||||
log.error(Utils.HEX.encode(sigBytes));
|
log.error(Utils.HEX.encode(sigBytes));
|
||||||
fail();
|
fail();
|
||||||
|
|||||||
@@ -759,7 +759,7 @@ public class FullBlockTestGenerator {
|
|||||||
try {
|
try {
|
||||||
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(73);
|
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(73);
|
||||||
bos.write(coinbaseOutKey.sign(hash).encodeToDER());
|
bos.write(coinbaseOutKey.sign(hash).encodeToDER());
|
||||||
bos.write(SigHash.SINGLE.ordinal() + 1);
|
bos.write(SigHash.SINGLE.value);
|
||||||
byte[] signature = bos.toByteArray();
|
byte[] signature = bos.toByteArray();
|
||||||
|
|
||||||
ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(signature.length + b39p2shScriptPubKey.length + 3);
|
ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(signature.length + b39p2shScriptPubKey.length + 3);
|
||||||
@@ -830,7 +830,7 @@ public class FullBlockTestGenerator {
|
|||||||
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(
|
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(
|
||||||
73);
|
73);
|
||||||
bos.write(coinbaseOutKey.sign(hash).encodeToDER());
|
bos.write(coinbaseOutKey.sign(hash).encodeToDER());
|
||||||
bos.write(SigHash.SINGLE.ordinal() + 1);
|
bos.write(SigHash.SINGLE.value);
|
||||||
byte[] signature = bos.toByteArray();
|
byte[] signature = bos.toByteArray();
|
||||||
|
|
||||||
ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(
|
ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(
|
||||||
|
|||||||
@@ -392,14 +392,14 @@ public class TransactionTest {
|
|||||||
|
|
||||||
final Transaction tx = block1.getTransactions().get(1);
|
final Transaction tx = block1.getTransactions().get(1);
|
||||||
final String txHash = tx.getHashAsString();
|
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++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
// ensure the transaction object itself was not modified; if it was, the hash will change
|
// ensure the transaction object itself was not modified; if it was, the hash will change
|
||||||
assertEquals(txHash, tx.getHashAsString());
|
assertEquals(txHash, tx.getHashAsString());
|
||||||
new Thread(){
|
new Thread(){
|
||||||
public void run() {
|
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());
|
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
|
||||||
|
|
||||||
byte[] refundSigCopy = Arrays.copyOf(refundSig, refundSig.length);
|
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 {
|
try {
|
||||||
clientV1State().provideRefundSignature(refundSigCopy, null);
|
clientV1State().provideRefundSignature(refundSigCopy, null);
|
||||||
fail();
|
fail();
|
||||||
@@ -627,7 +627,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
|||||||
totalPayment = totalPayment.add(size);
|
totalPayment = totalPayment.add(size);
|
||||||
|
|
||||||
byte[] signatureCopy = Arrays.copyOf(signature, signature.length);
|
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 {
|
try {
|
||||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy);
|
serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy);
|
||||||
fail();
|
fail();
|
||||||
@@ -659,7 +659,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
|||||||
assertEquals(totalPayment, halfCoin);
|
assertEquals(totalPayment, halfCoin);
|
||||||
|
|
||||||
signatureCopy = Arrays.copyOf(signature, signature.length);
|
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 {
|
try {
|
||||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy);
|
serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy);
|
||||||
fail();
|
fail();
|
||||||
|
|||||||
Reference in New Issue
Block a user