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:
parent
eda6dccf6d
commit
188cf6081d
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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]");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user