mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54:15 +00:00
Add support for creating multisig inputs scripts that redeem P2SH outputs
This commit is contained in:
parent
eda6dccf6d
commit
188cf6081d
@ -21,6 +21,7 @@ import com.google.bitcoin.core.ECKey;
|
|||||||
import com.google.bitcoin.crypto.TransactionSignature;
|
import com.google.bitcoin.crypto.TransactionSignature;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -116,10 +117,7 @@ public class ScriptBuilder {
|
|||||||
|
|
||||||
/** Create a program that satisfies an OP_CHECKMULTISIG program. */
|
/** Create a program that satisfies an OP_CHECKMULTISIG program. */
|
||||||
public static Script createMultiSigInputScript(List<TransactionSignature> signatures) {
|
public static Script createMultiSigInputScript(List<TransactionSignature> signatures) {
|
||||||
List<byte[]> sigs = new ArrayList<byte[]>(signatures.size());
|
return createP2SHMultiSigInputScript(signatures, null);
|
||||||
for (TransactionSignature signature : signatures)
|
|
||||||
sigs.add(signature.encodeToBitcoin());
|
|
||||||
return createMultiSigInputScriptBytes(sigs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a program that satisfies an OP_CHECKMULTISIG program. */
|
/** Create a program that satisfies an OP_CHECKMULTISIG program. */
|
||||||
@ -129,11 +127,30 @@ public class ScriptBuilder {
|
|||||||
|
|
||||||
/** Create a program that satisfies an OP_CHECKMULTISIG program, using pre-encoded signatures. */
|
/** Create a program that satisfies an OP_CHECKMULTISIG program, using pre-encoded signatures. */
|
||||||
public static Script createMultiSigInputScriptBytes(List<byte[]> signatures) {
|
public static Script createMultiSigInputScriptBytes(List<byte[]> signatures) {
|
||||||
|
return createMultiSigInputScriptBytes(signatures, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a program that satisfies a pay-to-script hashed OP_CHECKMULTISIG program. */
|
||||||
|
public static Script createP2SHMultiSigInputScript(List<TransactionSignature> signatures,
|
||||||
|
byte[] multisigProgramBytes) {
|
||||||
|
List<byte[]> sigs = new ArrayList<byte[]>(signatures.size());
|
||||||
|
for (TransactionSignature signature : signatures)
|
||||||
|
sigs.add(signature.encodeToBitcoin());
|
||||||
|
return createMultiSigInputScriptBytes(sigs, multisigProgramBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a program that satisfies an OP_CHECKMULTISIG program, using pre-encoded signatures.
|
||||||
|
* Optionally, appends the script program bytes if spending a P2SH output.
|
||||||
|
*/
|
||||||
|
public static Script createMultiSigInputScriptBytes(List<byte[]> signatures, @Nullable byte[] multisigProgramBytes) {
|
||||||
checkArgument(signatures.size() <= 16);
|
checkArgument(signatures.size() <= 16);
|
||||||
ScriptBuilder builder = new ScriptBuilder();
|
ScriptBuilder builder = new ScriptBuilder();
|
||||||
builder.smallNum(0); // Work around a bug in CHECKMULTISIG that is now a required part of the protocol.
|
builder.smallNum(0); // Work around a bug in CHECKMULTISIG that is now a required part of the protocol.
|
||||||
for (byte[] signature : signatures)
|
for (byte[] signature : signatures)
|
||||||
builder.data(signature);
|
builder.data(signature);
|
||||||
|
if (multisigProgramBytes!= null)
|
||||||
|
builder.data(multisigProgramBytes);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,10 +18,16 @@
|
|||||||
package com.google.bitcoin.script;
|
package com.google.bitcoin.script;
|
||||||
|
|
||||||
import com.google.bitcoin.core.*;
|
import com.google.bitcoin.core.*;
|
||||||
|
import com.google.bitcoin.core.Transaction.SigHash;
|
||||||
|
import com.google.bitcoin.crypto.TransactionSignature;
|
||||||
import com.google.bitcoin.params.MainNetParams;
|
import com.google.bitcoin.params.MainNetParams;
|
||||||
import com.google.bitcoin.params.TestNet3Params;
|
import com.google.bitcoin.params.TestNet3Params;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import org.hamcrest.core.IsEqual;
|
||||||
|
import org.hamcrest.core.IsNot;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
@ -30,6 +36,7 @@ import java.io.InputStreamReader;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -102,6 +109,50 @@ public class ScriptTest {
|
|||||||
assertTrue(s.isSentToRawPubKey());
|
assertTrue(s.isSentToRawPubKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateMultiSigInputScript() throws AddressFormatException {
|
||||||
|
// Setup transaction and signatures
|
||||||
|
ECKey key1 = new DumpedPrivateKey(params, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT").getKey();
|
||||||
|
ECKey key2 = new DumpedPrivateKey(params, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo").getKey();
|
||||||
|
ECKey key3 = new DumpedPrivateKey(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey();
|
||||||
|
Script multisigScript = ScriptBuilder.createMultiSigOutputScript(2, Arrays.asList(key1, key2, key3));
|
||||||
|
byte[] bytes = Hex.decode("01000000013df681ff83b43b6585fa32dd0e12b0b502e6481e04ee52ff0fdaf55a16a4ef61000000006b483045022100a84acca7906c13c5895a1314c165d33621cdcf8696145080895cbf301119b7cf0220730ff511106aa0e0a8570ff00ee57d7a6f24e30f592a10cae1deffac9e13b990012102b8d567bcd6328fd48a429f9cf4b315b859a58fd28c5088ef3cb1d98125fc4e8dffffffff02364f1c00000000001976a91439a02793b418de8ec748dd75382656453dc99bcb88ac40420f000000000017a9145780b80be32e117f675d6e0ada13ba799bf248e98700000000");
|
||||||
|
Transaction transaction = new Transaction(params, bytes);
|
||||||
|
TransactionOutput output = transaction.getOutput(1);
|
||||||
|
Transaction spendTx = new Transaction(params);
|
||||||
|
Address address = new Address(params, "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.getProgram());
|
||||||
|
|
||||||
|
// 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(IsEqual.equalTo(multisigScript.getProgram())));
|
||||||
|
}
|
||||||
|
|
||||||
private Script parseScriptString(String string) throws Exception {
|
private Script parseScriptString(String string) throws Exception {
|
||||||
String[] words = string.split("[ \\t\\n]");
|
String[] words = string.split("[ \\t\\n]");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user