mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-31 23:32:16 +00:00
Script: Introduce a builder class that makes it easier to assemble scripts without writing raw byte streams.
This commit is contained in:
parent
752e7006e5
commit
d113cbfc66
@ -17,6 +17,7 @@
|
|||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import com.google.bitcoin.script.Script;
|
import com.google.bitcoin.script.Script;
|
||||||
|
import com.google.bitcoin.script.ScriptBuilder;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -927,7 +928,8 @@ public class Block extends Message {
|
|||||||
// Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
|
// Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
|
||||||
// counter in the scriptSig so every transaction has a different hash.
|
// counter in the scriptSig so every transaction has a different hash.
|
||||||
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++, (byte) 1}));
|
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++, (byte) 1}));
|
||||||
coinbase.addOutput(new TransactionOutput(params, coinbase, value, Script.createOutputScript(pubKeyTo)));
|
coinbase.addOutput(new TransactionOutput(params, coinbase, value,
|
||||||
|
ScriptBuilder.createOutputScript(new ECKey(null, pubKeyTo)).getProgram()));
|
||||||
transactions.add(coinbase);
|
transactions.add(coinbase);
|
||||||
coinbase.setParent(this);
|
coinbase.setParent(this);
|
||||||
coinbase.length = coinbase.bitcoinSerialize().length;
|
coinbase.length = coinbase.bitcoinSerialize().length;
|
||||||
|
@ -18,6 +18,7 @@ package com.google.bitcoin.core;
|
|||||||
|
|
||||||
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
||||||
import com.google.bitcoin.script.Script;
|
import com.google.bitcoin.script.Script;
|
||||||
|
import com.google.bitcoin.script.ScriptBuilder;
|
||||||
import com.google.bitcoin.script.ScriptOpCodes;
|
import com.google.bitcoin.script.ScriptOpCodes;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -714,17 +715,16 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once a transaction has some inputs and outputs added, the signatures in the inputs can be calculated. The
|
* <p>Once a transaction has some inputs and outputs added, the signatures in the inputs can be calculated. The
|
||||||
* signature is over the transaction itself, to prove the redeemer actually created that transaction,
|
* signature is over the transaction itself, to prove the redeemer actually created that transaction,
|
||||||
* so we have to do this step last.<p>
|
* so we have to do this step last.</p>
|
||||||
* <p/>
|
|
||||||
* This method is similar to SignatureHash in script.cpp
|
|
||||||
*
|
*
|
||||||
* @param hashType This should always be set to SigHash.ALL currently. Other types are unused.
|
* @param hashType This should always be set to SigHash.ALL currently. Other types are unused.
|
||||||
* @param wallet A wallet is required to fetch the keys needed for signing.
|
* @param wallet A wallet is required to fetch the keys needed for signing.
|
||||||
* @param aesKey The AES key to use to decrypt the key before signing. Null if no decryption is required.
|
* @param aesKey The AES key to use to decrypt the key before signing. Null if no decryption is required.
|
||||||
*/
|
*/
|
||||||
public synchronized void signInputs(SigHash hashType, Wallet wallet, KeyParameter aesKey) throws ScriptException {
|
public synchronized void signInputs(SigHash hashType, Wallet wallet, KeyParameter aesKey) throws ScriptException {
|
||||||
|
// TODO: This should be a method of the TransactionInput that (possibly?) operates with a copy of this object.
|
||||||
Preconditions.checkState(inputs.size() > 0);
|
Preconditions.checkState(inputs.size() > 0);
|
||||||
Preconditions.checkState(outputs.size() > 0);
|
Preconditions.checkState(outputs.size() > 0);
|
||||||
|
|
||||||
@ -738,7 +738,8 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
// Note that each input may be claiming an output sent to a different key. So we have to look at the outputs
|
// Note that each input may be claiming an output sent to a different key. So we have to look at the outputs
|
||||||
// to figure out which key to sign with.
|
// to figure out which key to sign with.
|
||||||
|
|
||||||
byte[][] signatures = new byte[inputs.size()][];
|
int[] sigHashFlags = new int[inputs.size()];
|
||||||
|
ECKey.ECDSASignature[] signatures = new ECKey.ECDSASignature[inputs.size()];
|
||||||
ECKey[] signingKeys = new ECKey[inputs.size()];
|
ECKey[] signingKeys = new ECKey[inputs.size()];
|
||||||
for (int i = 0; i < inputs.size(); i++) {
|
for (int i = 0; i < inputs.size(); i++) {
|
||||||
TransactionInput input = inputs.get(i);
|
TransactionInput input = inputs.get(i);
|
||||||
@ -756,18 +757,10 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
byte[] connectedPubKeyScript = input.getOutpoint().getConnectedPubKeyScript();
|
byte[] connectedPubKeyScript = input.getOutpoint().getConnectedPubKeyScript();
|
||||||
Sha256Hash hash = hashTransactionForSignature(i, connectedPubKeyScript, hashType, anyoneCanPay);
|
Sha256Hash hash = hashTransactionForSignature(i, connectedPubKeyScript, hashType, anyoneCanPay);
|
||||||
|
|
||||||
// Now sign for the output so we can redeem it. We use the keypair to sign the hash,
|
// Now calculate the signatures we need to prove we own this transaction and are authorized to claim the
|
||||||
// and then put the resulting signature in the script along with the public key (below).
|
// associated money.
|
||||||
try {
|
signatures[i] = key.sign(hash, aesKey);
|
||||||
// Usually 71-73 bytes.
|
sigHashFlags[i] = (hashType.ordinal() + 1) | (anyoneCanPay ? 0x80 : 0);
|
||||||
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(73);
|
|
||||||
bos.write(key.sign(hash, aesKey).encodeToDER());
|
|
||||||
bos.write((hashType.ordinal() + 1) | (anyoneCanPay ? 0x80 : 0));
|
|
||||||
signatures[i] = bos.toByteArray();
|
|
||||||
bos.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we have calculated each signature, go through and create the scripts. Reminder: the script consists:
|
// Now we have calculated each signature, go through and create the scripts. Reminder: the script consists:
|
||||||
@ -777,12 +770,11 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
// 2) For pay-to-key outputs: just a signature.
|
// 2) For pay-to-key outputs: just a signature.
|
||||||
for (int i = 0; i < inputs.size(); i++) {
|
for (int i = 0; i < inputs.size(); i++) {
|
||||||
TransactionInput input = inputs.get(i);
|
TransactionInput input = inputs.get(i);
|
||||||
ECKey key = signingKeys[i];
|
|
||||||
Script scriptPubKey = input.getOutpoint().getConnectedOutput().getScriptPubKey();
|
Script scriptPubKey = input.getOutpoint().getConnectedOutput().getScriptPubKey();
|
||||||
if (scriptPubKey.isSentToAddress()) {
|
if (scriptPubKey.isSentToAddress()) {
|
||||||
input.setScriptBytes(Script.createInputScript(signatures[i], key.getPubKey()));
|
input.setScriptSig(ScriptBuilder.createInputScript(signatures[i], signingKeys[i], sigHashFlags[i]));
|
||||||
} else if (scriptPubKey.isSentToRawPubKey()) {
|
} else if (scriptPubKey.isSentToRawPubKey()) {
|
||||||
input.setScriptBytes(Script.createInputScript(signatures[i]));
|
input.setScriptSig(ScriptBuilder.createInputScript(signatures[i], sigHashFlags[i]));
|
||||||
} else {
|
} else {
|
||||||
// Should be unreachable - if we don't recognize the type of script we're trying to sign for, we should
|
// Should be unreachable - if we don't recognize the type of script we're trying to sign for, we should
|
||||||
// have failed above when fetching the key to sign with.
|
// have failed above when fetching the key to sign with.
|
||||||
|
@ -154,7 +154,8 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the input script.
|
* Returns the script that is fed to the referenced output (scriptPubKey) script in order to satisfy it: usually
|
||||||
|
* contains signatures and maybe keys, but can contain arbitrary data if the output script accepts it.
|
||||||
*/
|
*/
|
||||||
public Script getScriptSig() throws ScriptException {
|
public Script getScriptSig() throws ScriptException {
|
||||||
// Transactions that generate new coins don't actually have a script. Instead this
|
// Transactions that generate new coins don't actually have a script. Instead this
|
||||||
@ -166,6 +167,13 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
|||||||
return scriptSig;
|
return scriptSig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the given program as the scriptSig that is supposed to satisfy the connected output script. */
|
||||||
|
public void setScriptSig(Script scriptSig) {
|
||||||
|
this.scriptSig = checkNotNull(scriptSig);
|
||||||
|
// TODO: This should all be cleaned up so we have a consistent internal representation.
|
||||||
|
setScriptBytes(scriptSig.getProgram());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method that returns the from address of this input by parsing the scriptSig. The concept of a
|
* Convenience method that returns the from address of this input by parsing the scriptSig. The concept of a
|
||||||
* "from address" is not well defined in Bitcoin and you should not assume that senders of a transaction can
|
* "from address" is not well defined in Bitcoin and you should not assume that senders of a transaction can
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import com.google.bitcoin.script.Script;
|
import com.google.bitcoin.script.Script;
|
||||||
|
import com.google.bitcoin.script.ScriptBuilder;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
|||||||
* {@link Transaction#addOutput(java.math.BigInteger, Address)} instead of creating a TransactionOutput directly.
|
* {@link Transaction#addOutput(java.math.BigInteger, Address)} instead of creating a TransactionOutput directly.
|
||||||
*/
|
*/
|
||||||
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, Address to) {
|
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, Address to) {
|
||||||
this(params, parent, value, Script.createOutputScript(to));
|
this(params, parent, value, ScriptBuilder.createOutputScript(to).getProgram());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +101,7 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
|||||||
* {@link Transaction#addOutput(java.math.BigInteger, ECKey)} instead of creating an output directly.
|
* {@link Transaction#addOutput(java.math.BigInteger, ECKey)} instead of creating an output directly.
|
||||||
*/
|
*/
|
||||||
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, ECKey to) {
|
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, ECKey to) {
|
||||||
this(params, parent, value, Script.createOutputScript(to));
|
this(params, parent, value, ScriptBuilder.createOutputScript(to).getProgram());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, byte[] scriptBytes) {
|
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, byte[] scriptBytes) {
|
||||||
|
@ -60,10 +60,15 @@ public class Script {
|
|||||||
protected byte[] program;
|
protected byte[] program;
|
||||||
|
|
||||||
/** Creates an empty script that serializes to nothing. */
|
/** Creates an empty script that serializes to nothing. */
|
||||||
public Script() {
|
private Script() {
|
||||||
chunks = Lists.newArrayList();
|
chunks = Lists.newArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used from ScriptBuilder.
|
||||||
|
Script(List<ScriptChunk> chunks) {
|
||||||
|
this.chunks = Collections.unmodifiableList(new ArrayList<ScriptChunk>(chunks));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a Script that copies and wraps the programBytes array. The array is parsed and checked for syntactic
|
* Construct a Script that copies and wraps the programBytes array. The array is parsed and checked for syntactic
|
||||||
* validity.
|
* validity.
|
||||||
@ -267,44 +272,6 @@ public class Script {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] createOutputScript(Address to) {
|
|
||||||
try {
|
|
||||||
// TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes.
|
|
||||||
ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(24);
|
|
||||||
bits.write(OP_DUP);
|
|
||||||
bits.write(OP_HASH160);
|
|
||||||
writeBytes(bits, to.getHash160());
|
|
||||||
bits.write(OP_EQUALVERIFY);
|
|
||||||
bits.write(OP_CHECKSIG);
|
|
||||||
return bits.toByteArray();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a script that sends coins directly to the given public key (eg in a coinbase transaction).
|
|
||||||
*/
|
|
||||||
public static byte[] createOutputScript(byte[] pubkey) {
|
|
||||||
try {
|
|
||||||
// TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes.
|
|
||||||
ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(pubkey.length + 1);
|
|
||||||
writeBytes(bits, pubkey);
|
|
||||||
bits.write(OP_CHECKSIG);
|
|
||||||
return bits.toByteArray();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a script that sends coins directly to the given public key. Same as
|
|
||||||
* {@link Script#createOutputScript(byte[])} but more type safe.
|
|
||||||
*/
|
|
||||||
public static byte[] createOutputScript(ECKey pubkey) {
|
|
||||||
return createOutputScript(pubkey.getPubKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a program that requires at least N of the given keys to sign, using OP_CHECKMULTISIG. */
|
/** Creates a program that requires at least N of the given keys to sign, using OP_CHECKMULTISIG. */
|
||||||
public static byte[] createMultiSigOutputScript(int threshold, List<ECKey> pubkeys) {
|
public static byte[] createMultiSigOutputScript(int threshold, List<ECKey> pubkeys) {
|
||||||
checkArgument(threshold > 0);
|
checkArgument(threshold > 0);
|
||||||
@ -379,10 +346,10 @@ public class Script {
|
|||||||
return sigOps;
|
return sigOps;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int decodeFromOpN(byte opcode) {
|
static int decodeFromOpN(byte opcode) {
|
||||||
return decodeFromOpN(0xFF & opcode);
|
return decodeFromOpN(0xFF & opcode);
|
||||||
}
|
}
|
||||||
private static int decodeFromOpN(int opcode) {
|
static int decodeFromOpN(int opcode) {
|
||||||
checkArgument(opcode >= 0 && opcode <= OP_16, "decodeFromOpN called on non OP_N opcode");
|
checkArgument(opcode >= 0 && opcode <= OP_16, "decodeFromOpN called on non OP_N opcode");
|
||||||
if (opcode == OP_0)
|
if (opcode == OP_0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -390,10 +357,10 @@ public class Script {
|
|||||||
return opcode + 1 - OP_1;
|
return opcode + 1 - OP_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int encodeToOpN(byte value) {
|
static int encodeToOpN(byte value) {
|
||||||
return encodeToOpN(0xFF & value);
|
return encodeToOpN(0xFF & value);
|
||||||
}
|
}
|
||||||
private static int encodeToOpN(int value) {
|
static int encodeToOpN(int value) {
|
||||||
checkArgument(value >= 0 && value <= 16, "encodeToOpN called for a value we cannot encode in an opcode.");
|
checkArgument(value >= 0 && value <= 16, "encodeToOpN called for a value we cannot encode in an opcode.");
|
||||||
if (value == 0)
|
if (value == 0)
|
||||||
return OP_0;
|
return OP_0;
|
||||||
@ -1086,6 +1053,7 @@ public class Script {
|
|||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
// There is (at least) one exception that could be hit here (EOFException, if the sig is too short)
|
// There is (at least) one exception that could be hit here (EOFException, if the sig is too short)
|
||||||
// Because I can't verify there aren't more, we use a very generic Exception catch
|
// Because I can't verify there aren't more, we use a very generic Exception catch
|
||||||
|
log.warn(e1.toString());
|
||||||
sigValid = false;
|
sigValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
113
core/src/main/java/com/google/bitcoin/script/ScriptBuilder.java
Normal file
113
core/src/main/java/com/google/bitcoin/script/ScriptBuilder.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.bitcoin.script;
|
||||||
|
|
||||||
|
import com.google.bitcoin.core.Address;
|
||||||
|
import com.google.bitcoin.core.ECKey;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.bitcoin.script.ScriptOpCodes.*;
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Tools for the construction of commonly used script types. You don't normally need this as it's hidden behind
|
||||||
|
* convenience methods on {@link com.google.bitcoin.core.Transaction}, but they are useful when working with the
|
||||||
|
* protocol at a lower level.</p>
|
||||||
|
*/
|
||||||
|
public class ScriptBuilder {
|
||||||
|
private List<ScriptChunk> chunks;
|
||||||
|
|
||||||
|
public ScriptBuilder() {
|
||||||
|
chunks = Lists.newLinkedList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScriptBuilder op(int opcode) {
|
||||||
|
chunks.add(new ScriptChunk(true, new byte[]{(byte)opcode}));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScriptBuilder data(byte[] data) {
|
||||||
|
byte[] copy = Arrays.copyOf(data, data.length);
|
||||||
|
chunks.add(new ScriptChunk(false, copy));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScriptBuilder smallNum(byte num) {
|
||||||
|
checkArgument(num >= 0, "Cannot encode negative numbers with smallNum");
|
||||||
|
checkArgument(num <= 16, "Cannot encode numbers larger than 16 with smallNum");
|
||||||
|
chunks.add(new ScriptChunk(true, new byte[]{(byte)Script.encodeToOpN(num)}));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Script build() {
|
||||||
|
return new Script(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a scriptPubKey that encodes payment to the given address. */
|
||||||
|
public static Script createOutputScript(Address to) {
|
||||||
|
// OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
|
||||||
|
return new ScriptBuilder()
|
||||||
|
.op(OP_DUP)
|
||||||
|
.op(OP_HASH160)
|
||||||
|
.data(to.getHash160())
|
||||||
|
.op(OP_EQUALVERIFY)
|
||||||
|
.op(OP_CHECKSIG)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a scriptPubKey that encodes payment to the given raw public key. */
|
||||||
|
public static Script createOutputScript(ECKey key) {
|
||||||
|
return new ScriptBuilder().data(key.getPubKey()).op(OP_CHECKSIG).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] appendByte(byte[] buf, byte flags) {
|
||||||
|
byte[] result = Arrays.copyOf(buf, buf.length + 1);
|
||||||
|
result[result.length - 1] = flags;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a scriptSig that can redeem a pay-to-address output. */
|
||||||
|
public static Script createInputScript(ECKey.ECDSASignature signature, ECKey pubKey, int sigHashFlags) {
|
||||||
|
byte[] pubkeyBytes = pubKey.getPubKey();
|
||||||
|
byte[] sigBytes = appendByte(signature.encodeToDER(), (byte) sigHashFlags);
|
||||||
|
return new ScriptBuilder().data(sigBytes).data(pubkeyBytes).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a scriptSig that can redeem a pay-to-pubkey output. */
|
||||||
|
public static Script createInputScript(ECKey.ECDSASignature signature, int sigHashFlags) {
|
||||||
|
byte[] sigBytes = appendByte(signature.encodeToDER(), (byte) sigHashFlags);
|
||||||
|
return new ScriptBuilder().data(sigBytes).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a program that requires at least N of the given keys to sign, using OP_CHECKMULTISIG. */
|
||||||
|
public static Script createMultiSigOutputScript(int threshold, List<ECKey> pubkeys) {
|
||||||
|
checkArgument(threshold > 0);
|
||||||
|
checkArgument(threshold <= pubkeys.size());
|
||||||
|
checkArgument(pubkeys.size() <= 16); // That's the max we can represent with a single opcode.
|
||||||
|
ScriptBuilder builder = new ScriptBuilder();
|
||||||
|
builder.smallNum((byte)threshold);
|
||||||
|
for (ECKey key : pubkeys) {
|
||||||
|
builder.data(key.getPubKey());
|
||||||
|
}
|
||||||
|
builder.smallNum((byte)pubkeys.size());
|
||||||
|
builder.op(OP_CHECKMULTISIG);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package com.google.bitcoin.core;
|
|||||||
|
|
||||||
import com.google.bitcoin.core.Transaction.SigHash;
|
import com.google.bitcoin.core.Transaction.SigHash;
|
||||||
import com.google.bitcoin.script.Script;
|
import com.google.bitcoin.script.Script;
|
||||||
|
import com.google.bitcoin.script.ScriptBuilder;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -811,6 +812,7 @@ public class FullBlockTestGenerator {
|
|||||||
|
|
||||||
// A valid block created exactly like b44 to make sure the creation itself works
|
// A valid block created exactly like b44 to make sure the creation itself works
|
||||||
Block b44 = new Block(params);
|
Block b44 = new Block(params);
|
||||||
|
byte[] outScriptBytes = ScriptBuilder.createOutputScript(new ECKey(null, coinbaseOutKeyPubKey)).getProgram();
|
||||||
{
|
{
|
||||||
b44.setDifficultyTarget(b43.getDifficultyTarget());
|
b44.setDifficultyTarget(b43.getDifficultyTarget());
|
||||||
b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, BigInteger.ZERO);
|
b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, BigInteger.ZERO);
|
||||||
@ -818,7 +820,7 @@ public class FullBlockTestGenerator {
|
|||||||
Transaction t = new Transaction(params);
|
Transaction t = new Transaction(params);
|
||||||
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
|
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(0), new byte[] {OP_PUSHDATA1 - 1 }));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(0), new byte[] {OP_PUSHDATA1 - 1 }));
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(1), Script.createOutputScript(coinbaseOutKeyPubKey)));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(1), outScriptBytes));
|
||||||
// Spendable output
|
// Spendable output
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.ZERO, new byte[] {OP_1}));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.ZERO, new byte[] {OP_1}));
|
||||||
addOnlyInputToTransaction(t, out14);
|
addOnlyInputToTransaction(t, out14);
|
||||||
@ -841,7 +843,7 @@ public class FullBlockTestGenerator {
|
|||||||
Transaction t = new Transaction(params);
|
Transaction t = new Transaction(params);
|
||||||
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
|
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(0), new byte[] {OP_PUSHDATA1 - 1 }));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(0), new byte[] {OP_PUSHDATA1 - 1 }));
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(1), Script.createOutputScript(coinbaseOutKeyPubKey)));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(1), outScriptBytes));
|
||||||
// Spendable output
|
// Spendable output
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.ZERO, new byte[] {OP_1}));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.ZERO, new byte[] {OP_1}));
|
||||||
addOnlyInputToTransaction(t, out15);
|
addOnlyInputToTransaction(t, out15);
|
||||||
@ -917,7 +919,7 @@ public class FullBlockTestGenerator {
|
|||||||
{
|
{
|
||||||
Transaction coinbase = new Transaction(params);
|
Transaction coinbase = new Transaction(params);
|
||||||
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) 0xff, 110, 1}));
|
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) 0xff, 110, 1}));
|
||||||
coinbase.addOutput(new TransactionOutput(params, coinbase, BigInteger.ONE, Script.createOutputScript(coinbaseOutKeyPubKey)));
|
coinbase.addOutput(new TransactionOutput(params, coinbase, BigInteger.ONE, outScriptBytes));
|
||||||
b51.addTransaction(coinbase, false);
|
b51.addTransaction(coinbase, false);
|
||||||
}
|
}
|
||||||
b51.solve();
|
b51.solve();
|
||||||
@ -1273,7 +1275,8 @@ public class FullBlockTestGenerator {
|
|||||||
Transaction t = new Transaction(params);
|
Transaction t = new Transaction(params);
|
||||||
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
|
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(0), new byte[] {OP_PUSHDATA1 - 1 }));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(0), new byte[] {OP_PUSHDATA1 - 1 }));
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(1), Script.createOutputScript(coinbaseOutKeyPubKey)));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.valueOf(1),
|
||||||
|
ScriptBuilder.createOutputScript(new ECKey(null, coinbaseOutKeyPubKey)).getProgram()));
|
||||||
// Spendable output
|
// Spendable output
|
||||||
t.addOutput(new TransactionOutput(params, t, BigInteger.ZERO, new byte[] {OP_1}));
|
t.addOutput(new TransactionOutput(params, t, BigInteger.ZERO, new byte[] {OP_1}));
|
||||||
addOnlyInputToTransaction(t, prevOut);
|
addOnlyInputToTransaction(t, prevOut);
|
||||||
|
@ -66,9 +66,9 @@ public class ScriptTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMultiSig() throws Exception {
|
public void testMultiSig() throws Exception {
|
||||||
List<ECKey> keys = Lists.newArrayList(new ECKey(), new ECKey(), new ECKey());
|
List<ECKey> keys = Lists.newArrayList(new ECKey(), new ECKey(), new ECKey());
|
||||||
assertTrue(new Script(Script.createMultiSigOutputScript(2, keys)).isSentToMultiSig());
|
assertTrue(ScriptBuilder.createMultiSigOutputScript(2, keys).isSentToMultiSig());
|
||||||
assertTrue(new Script(Script.createMultiSigOutputScript(3, keys)).isSentToMultiSig());
|
assertTrue(ScriptBuilder.createMultiSigOutputScript(3, keys).isSentToMultiSig());
|
||||||
assertFalse(new Script(Script.createOutputScript(new ECKey())).isSentToMultiSig());
|
assertFalse(ScriptBuilder.createOutputScript(new ECKey()).isSentToMultiSig());
|
||||||
try {
|
try {
|
||||||
// Fail if we ask for more signatures than keys.
|
// Fail if we ask for more signatures than keys.
|
||||||
Script.createMultiSigOutputScript(4, keys);
|
Script.createMultiSigOutputScript(4, keys);
|
||||||
@ -82,6 +82,7 @@ public class ScriptTest {
|
|||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// Expected.
|
// Expected.
|
||||||
}
|
}
|
||||||
|
// Actual execution is tested by the data driven tests.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user