下面列出了怎么用org.bitcoinj.core.Transaction.SigHash的API类实例代码及写法,或者点击链接到github查看源代码。
private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException {
TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint);
input.setSequenceNumber(sequence);
t.addInput(input);
if (prevOut.scriptPubKey.getChunks().get(0).equalsOpCode(OP_TRUE)) {
input.setScriptSig(new ScriptBuilder().op(OP_1).build());
} else {
// Sign input
checkState(ScriptPattern.isPayToPubKey(prevOut.scriptPubKey));
Sha256Hash hash = t.hashForSignature(0, prevOut.scriptPubKey, SigHash.ALL, false);
input.setScriptSig(ScriptBuilder.createInputScript(
new TransactionSignature(coinbaseOutKey.sign(hash), SigHash.ALL, false))
);
}
}
private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException {
TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint);
input.setSequenceNumber(sequence);
t.addInput(input);
if (prevOut.scriptPubKey.getChunks().get(0).equalsOpCode(OP_TRUE)) {
input.setScriptSig(new ScriptBuilder().op(OP_1).build());
} else {
// Sign input
checkState(prevOut.scriptPubKey.isSentToRawPubKey());
Sha256Hash hash = t.hashForSignature(0, prevOut.scriptPubKey, SigHash.ALL, false);
input.setScriptSig(ScriptBuilder.createInputScript(
new TransactionSignature(coinbaseOutKey.sign(hash), SigHash.ALL, false))
);
}
}
protected static Object[] decodeModifier(String modifier) {
switch (modifier) {
case "AIAO":
return new Object[] { SigHash.ALL, Boolean.FALSE };
case "SIAO":
return new Object[] { SigHash.ALL, Boolean.TRUE };
case "AINO":
return new Object[] { SigHash.NONE, Boolean.FALSE };
case "SINO":
return new Object[] { SigHash.NONE, Boolean.TRUE };
case "AISO":
return new Object[] { SigHash.SINGLE, Boolean.FALSE };
case "SISO":
return new Object[] { SigHash.SINGLE, Boolean.TRUE };
}
throw new IllegalStateException(modifier);
}
private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException {
TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint);
input.setSequenceNumber(sequence);
t.addInput(input);
if (prevOut.scriptPubKey.getChunks().get(0).equalsOpCode(OP_TRUE)) {
input.setScriptSig(new ScriptBuilder().op(OP_1).build());
} else {
// Sign input
checkState(prevOut.scriptPubKey.isSentToRawPubKey());
Sha256Hash hash = t.hashForSignature(0, prevOut.scriptPubKey, SigHash.ALL, false);
input.setScriptSig(ScriptBuilder.createInputScript(
new TransactionSignature(coinbaseOutKey.sign(hash), SigHash.ALL, false))
);
}
}
/**
* Calculates the byte used in the protocol to represent the combination of mode and anyoneCanPay.
*/
public static int calcSigHashValue(Transaction.SigHash mode, boolean anyoneCanPay) {
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;
return sighashFlags;
}
/**
* Returns true if the given signature is has canonical encoding, and will thus be accepted as standard by
* Bitcoin Core. DER and the SIGHASH encoding allow for quite some flexibility in how the same structures
* are encoded, and this can open up novel attacks in which a man in the middle takes a transaction and then
* changes its signature such that the transaction hash is different but it's still valid. This can confuse wallets
* and generally violates people's mental model of how Bitcoin should work, thus, non-canonical signatures are now
* not relayed by default.
*/
public static boolean isEncodingCanonical(byte[] signature) {
// See Bitcoin Core's IsCanonicalSignature, https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
// Where R and S are not negative (their first byte has its highest bit not set), and not
// excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
// in which case a single 0 byte is necessary and even required).
if (signature.length < 9 || signature.length > 73)
return false;
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"
if ((signature[0] & 0xff) != 0x30 || (signature[1] & 0xff) != signature.length - 3)
return false;
int lenR = signature[3] & 0xff;
if (5 + lenR >= signature.length || lenR == 0)
return false;
int lenS = signature[5 + lenR] & 0xff;
if (lenR + lenS + 7 != signature.length || lenS == 0)
return false;
// R value type mismatch R value negative
if (signature[4 - 2] != 0x02 || (signature[4] & 0x80) == 0x80)
return false;
if (lenR > 1 && signature[4] == 0x00 && (signature[4 + 1] & 0x80) != 0x80)
return false; // R value excessively padded
// S value type mismatch S value negative
if (signature[6 + lenR - 2] != 0x02 || (signature[6 + lenR] & 0x80) == 0x80)
return false;
if (lenS > 1 && signature[6 + lenR] == 0x00 && (signature[6 + lenR + 1] & 0x80) != 0x80)
return false; // S value excessively padded
return true;
}
public Transaction.SigHash sigHashMode() {
final int mode = sighashFlags & 0x1f;
if (mode == Transaction.SigHash.NONE.value)
return Transaction.SigHash.NONE;
else if (mode == Transaction.SigHash.SINGLE.value)
return Transaction.SigHash.SINGLE;
else
return Transaction.SigHash.ALL;
}
/** Calculates the byte used in the protocol to represent the combination of mode and anyoneCanPay. */
public static int calcSigHashValue(Transaction.SigHash mode, boolean anyoneCanPay) {
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;
return sighashFlags;
}
/**
* Returns true if the given signature is has canonical encoding, and will thus be accepted as standard by
* Bitcoin Core. DER and the SIGHASH encoding allow for quite some flexibility in how the same structures
* are encoded, and this can open up novel attacks in which a man in the middle takes a transaction and then
* changes its signature such that the transaction hash is different but it's still valid. This can confuse wallets
* and generally violates people's mental model of how Bitcoin should work, thus, non-canonical signatures are now
* not relayed by default.
*/
public static boolean isEncodingCanonical(byte[] signature) {
// See Bitcoin Core's IsCanonicalSignature, https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
// Where R and S are not negative (their first byte has its highest bit not set), and not
// excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
// in which case a single 0 byte is necessary and even required).
if (signature.length < 9 || signature.length > 73)
return false;
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"
if ((signature[0] & 0xff) != 0x30 || (signature[1] & 0xff) != signature.length-3)
return false;
int lenR = signature[3] & 0xff;
if (5 + lenR >= signature.length || lenR == 0)
return false;
int lenS = signature[5+lenR] & 0xff;
if (lenR + lenS + 7 != signature.length || lenS == 0)
return false;
// R value type mismatch R value negative
if (signature[4-2] != 0x02 || (signature[4] & 0x80) == 0x80)
return false;
if (lenR > 1 && signature[4] == 0x00 && (signature[4+1] & 0x80) != 0x80)
return false; // R value excessively padded
// S value type mismatch S value negative
if (signature[6 + lenR - 2] != 0x02 || (signature[6 + lenR] & 0x80) == 0x80)
return false;
if (lenS > 1 && signature[6 + lenR] == 0x00 && (signature[6 + lenR + 1] & 0x80) != 0x80)
return false; // S value excessively padded
return true;
}
public Transaction.SigHash sigHashMode() {
final int mode = sighashFlags & 0x1f;
if (mode == Transaction.SigHash.NONE.value)
return Transaction.SigHash.NONE;
else if (mode == Transaction.SigHash.SINGLE.value)
return Transaction.SigHash.SINGLE;
else
return Transaction.SigHash.ALL;
}
@SuppressWarnings("unchecked")
public T signaturePlaceholder(String keyID, SigHash hashType, boolean anyoneCanPay) {
checkNotNull(keyID, "'keyID' cannot be null");
checkNotNull(hashType, "'hashType' cannot be null");
SignatureUtil sig = new SignatureUtil(keyID, hashType, anyoneCanPay);
String mapKey = sig.getUniqueKey();
byte[] data = (SIGNATURE_PREFIX + mapKey).getBytes();
checkState(data.length < 256, "data too long: " + data.length);
ScriptChunk chunk = new ScriptChunk(OP_PUSHDATA1, data);
super.addChunk(chunk);
this.signatures.put(mapKey, sig);
return (T) this;
}
@SuppressWarnings("incomplete-switch")
protected static String encodeModifier(SigHash sigHash, boolean anyoneCanPay) {
switch (sigHash) {
case ALL:
return (anyoneCanPay ? "SI" : "AI") + "AO";
case NONE:
return (anyoneCanPay ? "SI" : "AI") + "NO";
case SINGLE:
return (anyoneCanPay ? "SI" : "AI") + "SO";
}
throw new IllegalStateException();
}
@Test
public void test_signature() throws KeyStoreException {
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
assertEquals(0, sb.size());
assertEquals(0, sb.getFreeVariables().size());
assertEquals(0, sb.signatureSize());
PrivateKey k1 = PrivateKey.fresh(NetworkType.TESTNET);
PrivateKey k2 = PrivateKey.fresh(NetworkType.TESTNET);
String idK1 = ecks.addKey(k1);
String idK2 = ecks.addKey(k2);
sb.signaturePlaceholder(idK1, SigHash.ALL, false);
sb.signaturePlaceholder(idK2, SigHash.ALL, false);
System.out.println(sb);
assertEquals(2, sb.size());
assertEquals(0, sb.getFreeVariables().size());
assertEquals(2, sb.signatureSize());
Transaction tx = new Transaction(new MainNetParams());
tx.addInput(new TransactionInput(new MainNetParams(), null, new byte[] { 42, 42 }));
sb.setAllSignatures(ecks, tx, 0, new byte[] {}, false);
System.out.println(sb);
assertEquals(2, sb.size());
assertEquals(0, sb.getFreeVariables().size());
assertEquals(0, sb.signatureSize());
}
@Test
public void test_serialize_signature1() {
PrivateKey key = PrivateKey.fresh(NetworkType.TESTNET);
SigHash hashType = SigHash.ALL;
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
sb.number(15);
sb.signaturePlaceholder(ECKeyStore.getUniqueID(key), hashType, false);
String expected = "15 [sig," + ECKeyStore.getUniqueID(key) + ",AIAO]";
String actual = sb.serialize();
assertEquals(expected, actual);
}
@Test
public void test_serialize_signature2() {
PrivateKey key = PrivateKey.fresh(NetworkType.TESTNET);
SigHash hashType = SigHash.ALL;
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
sb.number(15);
sb.signaturePlaceholder(ECKeyStore.getUniqueID(key), hashType, true);
String expected = "15 [sig," + ECKeyStore.getUniqueID(key) + ",SIAO]";
String actual = sb.serialize();
assertEquals(expected, actual);
}
@Test
public void test_serialize_signature3() {
PrivateKey key = PrivateKey.fresh(NetworkType.TESTNET);
SigHash hashType = SigHash.SINGLE;
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
sb.number(15);
sb.signaturePlaceholder(ECKeyStore.getUniqueID(key), hashType, false);
String expected = "15 [sig," + ECKeyStore.getUniqueID(key) + ",AISO]";
String actual = sb.serialize();
assertEquals(expected, actual);
}
@Test
public void test_serialize_signature4() {
PrivateKey key = PrivateKey.fresh(NetworkType.TESTNET);
SigHash hashType = SigHash.SINGLE;
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
sb.number(15);
sb.signaturePlaceholder(ECKeyStore.getUniqueID(key), hashType, true);
String expected = "15 [sig," + ECKeyStore.getUniqueID(key) + ",SISO]";
String actual = sb.serialize();
assertEquals(expected, actual);
}
@Test
public void test_serialize_signature5() {
PrivateKey key = PrivateKey.fresh(NetworkType.TESTNET);
SigHash hashType = SigHash.NONE;
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
sb.number(15);
sb.signaturePlaceholder(ECKeyStore.getUniqueID(key), hashType, false);
String expected = "15 [sig," + ECKeyStore.getUniqueID(key) + ",AINO]";
String actual = sb.serialize();
assertEquals(expected, actual);
}
@Test
public void test_serialize_signature6() {
PrivateKey key = PrivateKey.fresh(NetworkType.TESTNET);
SigHash hashType = SigHash.NONE;
ScriptBuilderWithVar sb = new ScriptBuilderWithVar();
sb.number(15);
sb.signaturePlaceholder(ECKeyStore.getUniqueID(key), hashType, true);
String expected = "15 [sig," + ECKeyStore.getUniqueID(key) + ",SINO]";
String actual = sb.serialize();
assertEquals(expected, actual);
}
public static Script getMultisigInputScript (ECDSASignature client, ECDSASignature server) {
ArrayList<TransactionSignature> signList = new ArrayList<TransactionSignature>();
signList.add(new TransactionSignature(client, SigHash.ALL, false));
signList.add(new TransactionSignature(server, SigHash.ALL, false));
Script inputScript = ScriptBuilder.createMultiSigInputScript(signList);
/*
* Seems there is a bug here,
* https://groups.google.com/forum/#!topic/bitcoinj/A9R8TdUsXms
*/
Script workaround = new Script(inputScript.getProgram());
return workaround;
}
/** Calculates the byte used in the protocol to represent the combination of mode and anyoneCanPay. */
public static int calcSigHashValue(Transaction.SigHash mode, boolean anyoneCanPay) {
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;
return sighashFlags;
}
/**
* Returns true if the given signature is has canonical encoding, and will thus be accepted as standard by
* Bitcoin Core. DER and the SIGHASH encoding allow for quite some flexibility in how the same structures
* are encoded, and this can open up novel attacks in which a man in the middle takes a transaction and then
* changes its signature such that the transaction hash is different but it's still valid. This can confuse wallets
* and generally violates people's mental model of how Bitcoin should work, thus, non-canonical signatures are now
* not relayed by default.
*/
public static boolean isEncodingCanonical(byte[] signature) {
// See Bitcoin Core's IsCanonicalSignature, https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
// Where R and S are not negative (their first byte has its highest bit not set), and not
// excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
// in which case a single 0 byte is necessary and even required).
if (signature.length < 9 || signature.length > 73)
return false;
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"
if ((signature[0] & 0xff) != 0x30 || (signature[1] & 0xff) != signature.length-3)
return false;
int lenR = signature[3] & 0xff;
if (5 + lenR >= signature.length || lenR == 0)
return false;
int lenS = signature[5+lenR] & 0xff;
if (lenR + lenS + 7 != signature.length || lenS == 0)
return false;
// R value type mismatch R value negative
if (signature[4-2] != 0x02 || (signature[4] & 0x80) == 0x80)
return false;
if (lenR > 1 && signature[4] == 0x00 && (signature[4+1] & 0x80) != 0x80)
return false; // R value excessively padded
// S value type mismatch S value negative
if (signature[6 + lenR - 2] != 0x02 || (signature[6 + lenR] & 0x80) == 0x80)
return false;
if (lenS > 1 && signature[6 + lenR] == 0x00 && (signature[6 + lenR + 1] & 0x80) != 0x80)
return false; // S value excessively padded
return true;
}
public Transaction.SigHash sigHashMode() {
final int mode = sighashFlags & 0x1f;
if (mode == Transaction.SigHash.NONE.value)
return Transaction.SigHash.NONE;
else if (mode == Transaction.SigHash.SINGLE.value)
return Transaction.SigHash.SINGLE;
else
return Transaction.SigHash.ALL;
}
/**
* Gets the multisig input script.
*
* @param client the client
* @param server the server
* @return the multisig input script
*/
public static Script getMultisigInputScript (ECDSASignature client, ECDSASignature server) {
ArrayList<TransactionSignature> signList = new ArrayList<TransactionSignature>();
signList.add(new TransactionSignature(client, SigHash.ALL, false));
signList.add(new TransactionSignature(server, SigHash.ALL, false));
Script inputScript = ScriptBuilder.createMultiSigInputScript(signList);
/*
* Seems there is a bug here,
* https://groups.google.com/forum/#!topic/bitcoinj/A9R8TdUsXms
*/
Script workaround = new Script(inputScript.getProgram());
return workaround;
}
/**
* Constructs a signature with the given components and SIGHASH_ALL.
*/
public TransactionSignature(BigInteger r, BigInteger s) {
this(r, s, Transaction.SigHash.ALL.value);
}
/**
* Constructs a transaction signature based on the ECDSA signature.
*/
public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay) {
super(signature.r, signature.s);
sighashFlags = calcSigHashValue(mode, anyoneCanPay);
}
public boolean anyoneCanPay() {
return (sighashFlags & Transaction.SigHash.ANYONECANPAY.value) != 0;
}
@Test
public void testCreateMultiSigInputScript() {
// Setup transaction and signatures
ECKey key1 = DumpedPrivateKey.fromBase58(TESTNET, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT").getKey();
ECKey key2 = DumpedPrivateKey.fromBase58(TESTNET, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo").getKey();
ECKey key3 = DumpedPrivateKey.fromBase58(TESTNET, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey();
Script multisigScript = ScriptBuilder.createMultiSigOutputScript(2, Arrays.asList(key1, key2, key3));
byte[] bytes = HEX.decode("01000000013df681ff83b43b6585fa32dd0e12b0b502e6481e04ee52ff0fdaf55a16a4ef61000000006b483045022100a84acca7906c13c5895a1314c165d33621cdcf8696145080895cbf301119b7cf0220730ff511106aa0e0a8570ff00ee57d7a6f24e30f592a10cae1deffac9e13b990012102b8d567bcd6328fd48a429f9cf4b315b859a58fd28c5088ef3cb1d98125fc4e8dffffffff02364f1c00000000001976a91439a02793b418de8ec748dd75382656453dc99bcb88ac40420f000000000017a9145780b80be32e117f675d6e0ada13ba799bf248e98700000000");
Transaction transaction = TESTNET.getDefaultSerializer().makeTransaction(bytes);
TransactionOutput output = transaction.getOutput(1);
Transaction spendTx = new Transaction(TESTNET);
Address address = LegacyAddress.fromBase58(TESTNET, "n3CFiCmBXVt5d3HXKQ15EFZyhPz4yj5F3H");
Script outputScript = ScriptBuilder.createOutputScript(address);
spendTx.addOutput(output.getValue(), outputScript);
spendTx.addInput(output);
Sha256Hash sighash = spendTx.hashForSignature(0, multisigScript, SigHash.ALL, false);
ECKey.ECDSASignature party1Signature = key1.sign(sighash);
ECKey.ECDSASignature party2Signature = key2.sign(sighash);
TransactionSignature party1TransactionSignature = new TransactionSignature(party1Signature, SigHash.ALL, false);
TransactionSignature party2TransactionSignature = new TransactionSignature(party2Signature, SigHash.ALL, false);
// Create p2sh multisig input script
Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(party1TransactionSignature, party2TransactionSignature), multisigScript);
// Assert that the input script contains 4 chunks
assertTrue(inputScript.getChunks().size() == 4);
// Assert that the input script created contains the original multisig
// script as the last chunk
ScriptChunk scriptChunk = inputScript.getChunks().get(inputScript.getChunks().size() - 1);
Assert.assertArrayEquals(scriptChunk.data, multisigScript.getProgram());
// Create regular multisig input script
inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(party1TransactionSignature, party2TransactionSignature));
// Assert that the input script only contains 3 chunks
assertTrue(inputScript.getChunks().size() == 3);
// Assert that the input script created does not end with the original
// multisig script
scriptChunk = inputScript.getChunks().get(inputScript.getChunks().size() - 1);
Assert.assertThat(scriptChunk.data, IsNot.not(equalTo(multisigScript.getProgram())));
}
/** Constructs a signature with the given components and SIGHASH_ALL. */
public TransactionSignature(BigInteger r, BigInteger s) {
this(r, s, Transaction.SigHash.ALL.value);
}
/** Constructs a transaction signature based on the ECDSA signature. */
public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay) {
super(signature.r, signature.s);
sighashFlags = calcSigHashValue(mode, anyoneCanPay);
}