3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 15:22:16 +00:00

Add support for creating multisig inputs scripts that redeem P2SH outputs

This commit is contained in:
Jakob Stuber 2014-04-10 22:48:41 -05:00 committed by Mike Hearn
parent eda6dccf6d
commit 188cf6081d
2 changed files with 72 additions and 4 deletions

View File

@ -21,6 +21,7 @@ import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.crypto.TransactionSignature;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -116,10 +117,7 @@ public class ScriptBuilder {
/** Create a program that satisfies an OP_CHECKMULTISIG program. */
public static Script createMultiSigInputScript(List<TransactionSignature> signatures) {
List<byte[]> sigs = new ArrayList<byte[]>(signatures.size());
for (TransactionSignature signature : signatures)
sigs.add(signature.encodeToBitcoin());
return createMultiSigInputScriptBytes(sigs);
return createP2SHMultiSigInputScript(signatures, null);
}
/** 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. */
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);
ScriptBuilder builder = new ScriptBuilder();
builder.smallNum(0); // Work around a bug in CHECKMULTISIG that is now a required part of the protocol.
for (byte[] signature : signatures)
builder.data(signature);
if (multisigProgramBytes!= null)
builder.data(multisigProgramBytes);
return builder.build();
}

View File

@ -18,10 +18,16 @@
package com.google.bitcoin.script;
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.TestNet3Params;
import com.google.common.collect.ImmutableList;
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.spongycastle.util.encoders.Hex;
@ -30,6 +36,7 @@ import java.io.InputStreamReader;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -102,6 +109,50 @@ public class ScriptTest {
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 {
String[] words = string.split("[ \\t\\n]");