3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-13 02:35:52 +00:00

ScriptError: New error code for use in ScriptException. Script tests use this to assert for specific errors.

This also adds MINIMALDATA tests from Bitcoin Core into script_tests.json.
This commit is contained in:
Nicola Atzei 2017-07-11 09:53:15 +02:00 committed by Andreas Schildbach
parent bb176dab4d
commit d8af6b4be7
9 changed files with 370 additions and 127 deletions

View File

@ -16,14 +16,24 @@
package org.bitcoinj.core;
import org.bitcoinj.script.ScriptError;
@SuppressWarnings("serial")
public class ScriptException extends VerificationException {
public ScriptException(String msg) {
private final ScriptError err;
public ScriptException(ScriptError err, String msg) {
super(msg);
this.err = err;
}
public ScriptException(String msg, Exception e) {
public ScriptException(ScriptError err, String msg, Exception e) {
super(msg, e);
this.err = err;
}
public ScriptError getError() {
return err;
}
}

View File

@ -21,6 +21,7 @@ import org.bitcoinj.core.TransactionConfidence.ConfidenceType;
import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptError;
import org.bitcoinj.script.ScriptOpCodes;
import org.bitcoinj.signers.TransactionSigner;
import org.bitcoinj.utils.ExchangeRate;
@ -827,7 +828,7 @@ public class Transaction extends ChildMessage {
else if (scriptPubKey.isSentToAddress())
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
return input;
}

View File

@ -18,6 +18,7 @@
package org.bitcoinj.core;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptError;
import org.bitcoinj.wallet.DefaultRiskAnalysis;
import org.bitcoinj.wallet.KeyBag;
import org.bitcoinj.wallet.RedeemData;
@ -186,6 +187,7 @@ public class TransactionInput extends ChildMessage {
public Address getFromAddress() throws ScriptException {
if (isCoinBase()) {
throw new ScriptException(
ScriptError.SCRIPT_ERR_UNKNOWN_ERROR,
"This is a coinbase transaction which generates new coins. It does not have a from address.");
}
return getScriptSig().getFromAddress(params);

View File

@ -148,7 +148,7 @@ public class TransactionOutPoint extends ChildMessage {
byte[] pubkeyBytes = connectedScript.getPubKey();
return keyBag.findKeyFromPubKey(pubkeyBytes);
} else {
throw new ScriptException("Could not understand form of connected output script: " + connectedScript);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Could not understand form of connected output script: " + connectedScript);
}
}
@ -174,7 +174,7 @@ public class TransactionOutPoint extends ChildMessage {
byte[] scriptHash = connectedScript.getPubKeyHash();
return keyBag.findRedeemDataFromScriptHash(scriptHash);
} else {
throw new ScriptException("Could not understand form of connected output script: " + connectedScript);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Could not understand form of connected output script: " + connectedScript);
}
}

View File

@ -2,6 +2,7 @@
* Copyright 2011 Google Inc.
* Copyright 2012 Matt Corallo.
* Copyright 2014 Andreas Schildbach
* Copyright 2017 Nicola Atzei
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -190,16 +191,16 @@ public class Script {
// Read some bytes of data, where how many is the opcode value itself.
dataToRead = opcode;
} else if (opcode == OP_PUSHDATA1) {
if (bis.available() < 1) throw new ScriptException("Unexpected end of script");
if (bis.available() < 1) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script");
dataToRead = bis.read();
} else if (opcode == OP_PUSHDATA2) {
// Read a short, then read that many bytes of data.
if (bis.available() < 2) throw new ScriptException("Unexpected end of script");
if (bis.available() < 2) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script");
dataToRead = bis.read() | (bis.read() << 8);
} else if (opcode == OP_PUSHDATA4) {
// Read a uint32, then read that many bytes of data.
// Though this is allowed, because its value cannot be > 520, it should never actually be used
if (bis.available() < 4) throw new ScriptException("Unexpected end of script");
if (bis.available() < 4) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script");
dataToRead = ((long)bis.read()) | (((long)bis.read()) << 8) | (((long)bis.read()) << 16) | (((long)bis.read()) << 24);
}
@ -208,7 +209,7 @@ public class Script {
chunk = new ScriptChunk(opcode, null, startLocationInProgram);
} else {
if (dataToRead > bis.available())
throw new ScriptException("Push of data element that is larger than remaining data");
throw new ScriptException(ScriptError.SCRIPT_ERR_BAD_OPCODE, "Push of data element that is larger than remaining data");
byte[] data = new byte[(int)dataToRead];
checkState(dataToRead == 0 || bis.read(data, 0, (int)dataToRead) == dataToRead);
chunk = new ScriptChunk(opcode, data, startLocationInProgram);
@ -273,7 +274,7 @@ public class Script {
else if (isPayToScriptHash())
return chunks.get(1).data;
else
throw new ScriptException("Script not in the standard scriptPubKey form");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not in the standard scriptPubKey form");
}
/**
@ -286,7 +287,7 @@ public class Script {
*/
public byte[] getPubKey() throws ScriptException {
if (chunks.size() != 2) {
throw new ScriptException("Script not of right size, expecting 2 but got " + chunks.size());
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not of right size, expecting 2 but got " + chunks.size());
}
final ScriptChunk chunk0 = chunks.get(0);
final byte[] chunk0data = chunk0.data;
@ -299,7 +300,7 @@ public class Script {
// A large constant followed by an OP_CHECKSIG is the key.
return chunk0data;
} else {
throw new ScriptException("Script did not match expected form: " + this);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script did not match expected form: " + this);
}
}
@ -310,7 +311,7 @@ public class Script {
*/
public byte[] getCLTVPaymentChannelSenderPubKey() throws ScriptException {
if (!isSentToCLTVPaymentChannel()) {
throw new ScriptException("Script not a standard CHECKLOCKTIMVERIFY transaction: " + this);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not a standard CHECKLOCKTIMVERIFY transaction: " + this);
}
return chunks.get(8).data;
}
@ -322,16 +323,16 @@ public class Script {
*/
public byte[] getCLTVPaymentChannelRecipientPubKey() throws ScriptException {
if (!isSentToCLTVPaymentChannel()) {
throw new ScriptException("Script not a standard CHECKLOCKTIMVERIFY transaction: " + this);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not a standard CHECKLOCKTIMVERIFY transaction: " + this);
}
return chunks.get(1).data;
}
public BigInteger getCLTVPaymentChannelExpiry() {
if (!isSentToCLTVPaymentChannel()) {
throw new ScriptException("Script not a standard CHECKLOCKTIMEVERIFY transaction: " + this);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not a standard CHECKLOCKTIMEVERIFY transaction: " + this);
}
return castToBigInteger(chunks.get(4).data, 5);
return castToBigInteger(chunks.get(4).data, 5, false);
}
/**
@ -366,7 +367,7 @@ public class Script {
else if (forcePayToPubKey && isSentToRawPubKey())
return ECKey.fromPublicOnly(getPubKey()).toAddress(params);
else
throw new ScriptException("Cannot cast this script to a pay-to-address type");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Cannot cast this script to a pay-to-address type");
}
////////////////////// Interface for writing scripts from scratch ////////////////////////////////
@ -454,7 +455,7 @@ public class Script {
checkArgument(redeemScript != null, "Redeem script required to create P2SH input script");
return ScriptBuilder.createP2SHMultiSigInputScript(null, redeemScript);
} else {
throw new ScriptException("Do not understand script type: " + this);
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Do not understand script type: " + this);
}
}
@ -522,7 +523,7 @@ public class Script {
*/
public List<ECKey> getPubKeys() {
if (!isSentToMultiSig())
throw new ScriptException("Only usable for multisig scripts.");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Only usable for multisig scripts.");
ArrayList<ECKey> result = Lists.newArrayList();
int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode);
@ -808,10 +809,8 @@ public class Script {
* sizes.
* @throws ScriptException if the chunk is longer than 4 bytes.
*/
private static BigInteger castToBigInteger(byte[] chunk) throws ScriptException {
if (chunk.length > 4)
throw new ScriptException("Script attempted to use an integer larger than 4 bytes");
return Utils.decodeMPI(Utils.reverseBytes(chunk), false);
private static BigInteger castToBigInteger(byte[] chunk, final boolean requireMinimal) throws ScriptException {
return castToBigInteger(chunk, 4, requireMinimal);
}
/**
@ -820,12 +819,32 @@ public class Script {
* the normal maximum length does not apply (i.e. CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY).
*
* @param maxLength the maximum length in bytes.
* @param requireMinimal check if the number is encoded with the minimum possible number of bytes
* @throws ScriptException if the chunk is longer than the specified maximum.
*/
private static BigInteger castToBigInteger(final byte[] chunk, final int maxLength) throws ScriptException {
private static BigInteger castToBigInteger(final byte[] chunk, final int maxLength, final boolean requireMinimal) throws ScriptException {
if (chunk.length > maxLength)
throw new ScriptException("Script attempted to use an integer larger than "
+ maxLength + " bytes");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script attempted to use an integer larger than " + maxLength + " bytes");
if (requireMinimal && chunk.length > 0) {
// Check that the number is encoded with the minimum possible
// number of bytes.
//
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if ((chunk[chunk.length - 1] & 0x7f) == 0) {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if (chunk.length <= 1 || (chunk[chunk.length - 2] & 0x80) == 0) {
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "non-minimally encoded script number");
}
}
}
return Utils.decodeMPI(Utils.reverseBytes(chunk), false);
}
@ -875,31 +894,39 @@ public class Script {
if (!shouldExecute)
continue;
// Check minimal push
if (verifyFlags.contains(VerifyFlag.MINIMALDATA) && !chunk.isShortestPossiblePushData())
throw new ScriptException(ScriptError.SCRIPT_ERR_MINIMALDATA, "Script included a not minimal push operation.");
stack.add(new byte[] {});
} else if (!chunk.isOpCode()) {
if (chunk.data.length > MAX_SCRIPT_ELEMENT_SIZE)
throw new ScriptException("Attempted to push a data string larger than 520 bytes");
throw new ScriptException(ScriptError.SCRIPT_ERR_PUSH_SIZE, "Attempted to push a data string larger than 520 bytes");
if (!shouldExecute)
continue;
// Check minimal push
if (verifyFlags.contains(VerifyFlag.MINIMALDATA) && !chunk.isShortestPossiblePushData())
throw new ScriptException(ScriptError.SCRIPT_ERR_MINIMALDATA, "Script included a not minimal push operation.");
stack.add(chunk.data);
} else {
int opcode = chunk.opcode;
if (opcode > OP_16) {
opCount++;
if (opCount > 201)
throw new ScriptException("More script operations than is allowed");
throw new ScriptException(ScriptError.SCRIPT_ERR_OP_COUNT, "More script operations than is allowed");
}
if (opcode == OP_VERIF || opcode == OP_VERNOTIF)
throw new ScriptException("Script included OP_VERIF or OP_VERNOTIF");
throw new ScriptException(ScriptError.SCRIPT_ERR_BAD_OPCODE, "Script included OP_VERIF or OP_VERNOTIF");
if (opcode == OP_CAT || opcode == OP_SUBSTR || opcode == OP_LEFT || opcode == OP_RIGHT ||
opcode == OP_INVERT || opcode == OP_AND || opcode == OP_OR || opcode == OP_XOR ||
opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_DIV ||
opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT)
throw new ScriptException("Script included a disabled Script Op.");
throw new ScriptException(ScriptError.SCRIPT_ERR_DISABLED_OPCODE, "Script included a disabled Script Op.");
switch (opcode) {
case OP_IF:
@ -908,7 +935,7 @@ public class Script {
continue;
}
if (stack.size() < 1)
throw new ScriptException("Attempted OP_IF on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNBALANCED_CONDITIONAL, "Attempted OP_IF on an empty stack");
ifStack.add(castToBool(stack.pollLast()));
continue;
case OP_NOTIF:
@ -917,17 +944,17 @@ public class Script {
continue;
}
if (stack.size() < 1)
throw new ScriptException("Attempted OP_NOTIF on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNBALANCED_CONDITIONAL, "Attempted OP_NOTIF on an empty stack");
ifStack.add(!castToBool(stack.pollLast()));
continue;
case OP_ELSE:
if (ifStack.isEmpty())
throw new ScriptException("Attempted OP_ELSE without OP_IF/NOTIF");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNBALANCED_CONDITIONAL, "Attempted OP_ELSE without OP_IF/NOTIF");
ifStack.add(!ifStack.pollLast());
continue;
case OP_ENDIF:
if (ifStack.isEmpty())
throw new ScriptException("Attempted OP_ENDIF without OP_IF/NOTIF");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNBALANCED_CONDITIONAL, "Attempted OP_ENDIF without OP_IF/NOTIF");
ifStack.pollLast();
continue;
}
@ -962,31 +989,31 @@ public class Script {
break;
case OP_VERIFY:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_VERIFY on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_VERIFY on an empty stack");
if (!castToBool(stack.pollLast()))
throw new ScriptException("OP_VERIFY failed");
throw new ScriptException(ScriptError.SCRIPT_ERR_VERIFY, "OP_VERIFY failed");
break;
case OP_RETURN:
throw new ScriptException("Script called OP_RETURN");
throw new ScriptException(ScriptError.SCRIPT_ERR_OP_RETURN, "Script called OP_RETURN");
case OP_TOALTSTACK:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_TOALTSTACK on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_TOALTSTACK on an empty stack");
altstack.add(stack.pollLast());
break;
case OP_FROMALTSTACK:
if (altstack.size() < 1)
throw new ScriptException("Attempted OP_FROMALTSTACK on an empty altstack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, "Attempted OP_FROMALTSTACK on an empty altstack");
stack.add(altstack.pollLast());
break;
case OP_2DROP:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_2DROP on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_2DROP on a stack with size < 2");
stack.pollLast();
stack.pollLast();
break;
case OP_2DUP:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_2DUP on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_2DUP on a stack with size < 2");
Iterator<byte[]> it2DUP = stack.descendingIterator();
byte[] OP2DUPtmpChunk2 = it2DUP.next();
stack.add(it2DUP.next());
@ -994,7 +1021,7 @@ public class Script {
break;
case OP_3DUP:
if (stack.size() < 3)
throw new ScriptException("Attempted OP_3DUP on a stack with size < 3");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_3DUP on a stack with size < 3");
Iterator<byte[]> it3DUP = stack.descendingIterator();
byte[] OP3DUPtmpChunk3 = it3DUP.next();
byte[] OP3DUPtmpChunk2 = it3DUP.next();
@ -1004,7 +1031,7 @@ public class Script {
break;
case OP_2OVER:
if (stack.size() < 4)
throw new ScriptException("Attempted OP_2OVER on a stack with size < 4");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_2OVER on a stack with size < 4");
Iterator<byte[]> it2OVER = stack.descendingIterator();
it2OVER.next();
it2OVER.next();
@ -1014,7 +1041,7 @@ public class Script {
break;
case OP_2ROT:
if (stack.size() < 6)
throw new ScriptException("Attempted OP_2ROT on a stack with size < 6");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_2ROT on a stack with size < 6");
byte[] OP2ROTtmpChunk6 = stack.pollLast();
byte[] OP2ROTtmpChunk5 = stack.pollLast();
byte[] OP2ROTtmpChunk4 = stack.pollLast();
@ -1030,7 +1057,7 @@ public class Script {
break;
case OP_2SWAP:
if (stack.size() < 4)
throw new ScriptException("Attempted OP_2SWAP on a stack with size < 4");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_2SWAP on a stack with size < 4");
byte[] OP2SWAPtmpChunk4 = stack.pollLast();
byte[] OP2SWAPtmpChunk3 = stack.pollLast();
byte[] OP2SWAPtmpChunk2 = stack.pollLast();
@ -1042,7 +1069,7 @@ public class Script {
break;
case OP_IFDUP:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_IFDUP on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_IFDUP on an empty stack");
if (castToBool(stack.getLast()))
stack.add(stack.getLast());
break;
@ -1051,24 +1078,24 @@ public class Script {
break;
case OP_DROP:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_DROP on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_DROP on an empty stack");
stack.pollLast();
break;
case OP_DUP:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_DUP on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_DUP on an empty stack");
stack.add(stack.getLast());
break;
case OP_NIP:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_NIP on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_NIP on a stack with size < 2");
byte[] OPNIPtmpChunk = stack.pollLast();
stack.pollLast();
stack.add(OPNIPtmpChunk);
break;
case OP_OVER:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_OVER on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_OVER on a stack with size < 2");
Iterator<byte[]> itOVER = stack.descendingIterator();
itOVER.next();
stack.add(itOVER.next());
@ -1076,10 +1103,10 @@ public class Script {
case OP_PICK:
case OP_ROLL:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_PICK/OP_ROLL on an empty stack");
long val = castToBigInteger(stack.pollLast()).longValue();
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_PICK/OP_ROLL on an empty stack");
long val = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA)).longValue();
if (val < 0 || val >= stack.size())
throw new ScriptException("OP_PICK/OP_ROLL attempted to get data deeper than stack size");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "OP_PICK/OP_ROLL attempted to get data deeper than stack size");
Iterator<byte[]> itPICK = stack.descendingIterator();
for (long i = 0; i < val; i++)
itPICK.next();
@ -1090,7 +1117,7 @@ public class Script {
break;
case OP_ROT:
if (stack.size() < 3)
throw new ScriptException("Attempted OP_ROT on a stack with size < 3");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_ROT on a stack with size < 3");
byte[] OPROTtmpChunk3 = stack.pollLast();
byte[] OPROTtmpChunk2 = stack.pollLast();
byte[] OPROTtmpChunk1 = stack.pollLast();
@ -1101,7 +1128,7 @@ public class Script {
case OP_SWAP:
case OP_TUCK:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_SWAP on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_SWAP on a stack with size < 2");
byte[] OPSWAPtmpChunk2 = stack.pollLast();
byte[] OPSWAPtmpChunk1 = stack.pollLast();
stack.add(OPSWAPtmpChunk2);
@ -1113,27 +1140,27 @@ public class Script {
case OP_SUBSTR:
case OP_LEFT:
case OP_RIGHT:
throw new ScriptException("Attempted to use disabled Script Op.");
throw new ScriptException(ScriptError.SCRIPT_ERR_DISABLED_OPCODE, "Attempted to use disabled Script Op.");
case OP_SIZE:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_SIZE on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_SIZE on an empty stack");
stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(stack.getLast().length), false)));
break;
case OP_INVERT:
case OP_AND:
case OP_OR:
case OP_XOR:
throw new ScriptException("Attempted to use disabled Script Op.");
throw new ScriptException(ScriptError.SCRIPT_ERR_DISABLED_OPCODE, "Attempted to use disabled Script Op.");
case OP_EQUAL:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_EQUAL on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_EQUAL on a stack with size < 2");
stack.add(Arrays.equals(stack.pollLast(), stack.pollLast()) ? new byte[] {1} : new byte[] {});
break;
case OP_EQUALVERIFY:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_EQUALVERIFY on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_EQUALVERIFY on a stack with size < 2");
if (!Arrays.equals(stack.pollLast(), stack.pollLast()))
throw new ScriptException("OP_EQUALVERIFY: non-equal data");
throw new ScriptException(ScriptError.SCRIPT_ERR_EQUALVERIFY, "OP_EQUALVERIFY: non-equal data");
break;
case OP_1ADD:
case OP_1SUB:
@ -1142,8 +1169,8 @@ public class Script {
case OP_NOT:
case OP_0NOTEQUAL:
if (stack.size() < 1)
throw new ScriptException("Attempted a numeric op on an empty stack");
BigInteger numericOPnum = castToBigInteger(stack.pollLast());
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted a numeric op on an empty stack");
BigInteger numericOPnum = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
switch (opcode) {
case OP_1ADD:
@ -1179,7 +1206,7 @@ public class Script {
break;
case OP_2MUL:
case OP_2DIV:
throw new ScriptException("Attempted to use disabled Script Op.");
throw new ScriptException(ScriptError.SCRIPT_ERR_DISABLED_OPCODE, "Attempted to use disabled Script Op.");
case OP_ADD:
case OP_SUB:
case OP_BOOLAND:
@ -1193,9 +1220,9 @@ public class Script {
case OP_MIN:
case OP_MAX:
if (stack.size() < 2)
throw new ScriptException("Attempted a numeric op on a stack with size < 2");
BigInteger numericOPnum2 = castToBigInteger(stack.pollLast());
BigInteger numericOPnum1 = castToBigInteger(stack.pollLast());
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted a numeric op on a stack with size < 2");
BigInteger numericOPnum2 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger numericOPnum1 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger numericOPresult;
switch (opcode) {
@ -1276,22 +1303,22 @@ public class Script {
case OP_MOD:
case OP_LSHIFT:
case OP_RSHIFT:
throw new ScriptException("Attempted to use disabled Script Op.");
throw new ScriptException(ScriptError.SCRIPT_ERR_DISABLED_OPCODE, "Attempted to use disabled Script Op.");
case OP_NUMEQUALVERIFY:
if (stack.size() < 2)
throw new ScriptException("Attempted OP_NUMEQUALVERIFY on a stack with size < 2");
BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger(stack.pollLast());
BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger(stack.pollLast());
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_NUMEQUALVERIFY on a stack with size < 2");
BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
if (!OPNUMEQUALVERIFYnum1.equals(OPNUMEQUALVERIFYnum2))
throw new ScriptException("OP_NUMEQUALVERIFY failed");
throw new ScriptException(ScriptError.SCRIPT_ERR_NUMEQUALVERIFY, "OP_NUMEQUALVERIFY failed");
break;
case OP_WITHIN:
if (stack.size() < 3)
throw new ScriptException("Attempted OP_WITHIN on a stack with size < 3");
BigInteger OPWITHINnum3 = castToBigInteger(stack.pollLast());
BigInteger OPWITHINnum2 = castToBigInteger(stack.pollLast());
BigInteger OPWITHINnum1 = castToBigInteger(stack.pollLast());
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_WITHIN on a stack with size < 3");
BigInteger OPWITHINnum3 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger OPWITHINnum2 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger OPWITHINnum1 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
if (OPWITHINnum2.compareTo(OPWITHINnum1) <= 0 && OPWITHINnum1.compareTo(OPWITHINnum3) < 0)
stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ONE, false)));
else
@ -1299,7 +1326,7 @@ public class Script {
break;
case OP_RIPEMD160:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_RIPEMD160 on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_RIPEMD160 on an empty stack");
RIPEMD160Digest digest = new RIPEMD160Digest();
byte[] dataToHash = stack.pollLast();
digest.update(dataToHash, 0, dataToHash.length);
@ -1309,7 +1336,7 @@ public class Script {
break;
case OP_SHA1:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_SHA1 on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_SHA1 on an empty stack");
try {
stack.add(MessageDigest.getInstance("SHA-1").digest(stack.pollLast()));
} catch (NoSuchAlgorithmException e) {
@ -1318,17 +1345,17 @@ public class Script {
break;
case OP_SHA256:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_SHA256 on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_SHA256 on an empty stack");
stack.add(Sha256Hash.hash(stack.pollLast()));
break;
case OP_HASH160:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_HASH160 on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_HASH160 on an empty stack");
stack.add(Utils.sha256hash160(stack.pollLast()));
break;
case OP_HASH256:
if (stack.size() < 1)
throw new ScriptException("Attempted OP_SHA256 on an empty stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_SHA256 on an empty stack");
stack.add(Sha256Hash.hashTwice(stack.pollLast()));
break;
case OP_CODESEPARATOR:
@ -1350,21 +1377,21 @@ public class Script {
if (!verifyFlags.contains(VerifyFlag.CHECKLOCKTIMEVERIFY)) {
// not enabled; treat as a NOP2
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) {
throw new ScriptException("Script used a reserved opcode " + opcode);
throw new ScriptException(ScriptError.SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "Script used a reserved opcode " + opcode);
}
break;
}
executeCheckLockTimeVerify(txContainingThis, (int) index, stack);
executeCheckLockTimeVerify(txContainingThis, (int) index, stack, verifyFlags);
break;
case OP_CHECKSEQUENCEVERIFY:
if (!verifyFlags.contains(VerifyFlag.CHECKSEQUENCEVERIFY)) {
// not enabled; treat as a NOP3
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) {
throw new ScriptException("Script used a reserved opcode " + opcode);
throw new ScriptException(ScriptError.SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "Script used a reserved opcode " + opcode);
}
break;
}
executeCheckSequenceVerify(txContainingThis, (int) index, stack);
executeCheckSequenceVerify(txContainingThis, (int) index, stack, verifyFlags);
break;
case OP_NOP1:
case OP_NOP4:
@ -1375,46 +1402,46 @@ public class Script {
case OP_NOP9:
case OP_NOP10:
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) {
throw new ScriptException("Script used a reserved opcode " + opcode);
throw new ScriptException(ScriptError.SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "Script used a reserved opcode " + opcode);
}
break;
default:
throw new ScriptException("Script used a reserved opcode " + opcode);
throw new ScriptException(ScriptError.SCRIPT_ERR_BAD_OPCODE, "Script used a reserved opcode " + opcode);
}
}
if (stack.size() + altstack.size() > 1000 || stack.size() + altstack.size() < 0)
throw new ScriptException("Stack size exceeded range");
throw new ScriptException(ScriptError.SCRIPT_ERR_STACK_SIZE, "Stack size exceeded range");
}
if (!ifStack.isEmpty())
throw new ScriptException("OP_IF/OP_NOTIF without OP_ENDIF");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNBALANCED_CONDITIONAL, "OP_IF/OP_NOTIF without OP_ENDIF");
}
// This is more or less a direct translation of the code in Bitcoin Core
private static void executeCheckLockTimeVerify(Transaction txContainingThis, int index, LinkedList<byte[]> stack) throws ScriptException {
private static void executeCheckLockTimeVerify(Transaction txContainingThis, int index, LinkedList<byte[]> stack, Set<VerifyFlag> verifyFlags) throws ScriptException {
if (stack.size() < 1)
throw new ScriptException("Attempted OP_CHECKLOCKTIMEVERIFY on a stack with size < 1");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKLOCKTIMEVERIFY on a stack with size < 1");
// Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums to avoid year 2038 issue.
final BigInteger nLockTime = castToBigInteger(stack.getLast(), 5);
final BigInteger nLockTime = castToBigInteger(stack.getLast(), 5, verifyFlags.contains(VerifyFlag.MINIMALDATA));
if (nLockTime.compareTo(BigInteger.ZERO) < 0)
throw new ScriptException("Negative locktime");
throw new ScriptException(ScriptError.SCRIPT_ERR_NEGATIVE_LOCKTIME, "Negative locktime");
// There are two kinds of nLockTime, need to ensure we're comparing apples-to-apples
if (!(
((txContainingThis.getLockTime() < Transaction.LOCKTIME_THRESHOLD) && (nLockTime.compareTo(Transaction.LOCKTIME_THRESHOLD_BIG)) < 0) ||
((txContainingThis.getLockTime() >= Transaction.LOCKTIME_THRESHOLD) && (nLockTime.compareTo(Transaction.LOCKTIME_THRESHOLD_BIG)) >= 0))
)
throw new ScriptException("Locktime requirement type mismatch");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNSATISFIED_LOCKTIME, "Locktime requirement type mismatch");
// Now that we know we're comparing apples-to-apples, the
// comparison is a simple numeric one.
if (nLockTime.compareTo(BigInteger.valueOf(txContainingThis.getLockTime())) > 0)
throw new ScriptException("Locktime requirement not satisfied");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNSATISFIED_LOCKTIME, "Locktime requirement not satisfied");
// Finally the nLockTime feature can be disabled and thus
// CHECKLOCKTIMEVERIFY bypassed if every txin has been
@ -1427,12 +1454,12 @@ public class Script {
// inputs, but testing just this input minimizes the data
// required to prove correct CHECKLOCKTIMEVERIFY execution.
if (!txContainingThis.getInput(index).hasSequence())
throw new ScriptException("Transaction contains a final transaction input for a CHECKLOCKTIMEVERIFY script.");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNSATISFIED_LOCKTIME, "Transaction contains a final transaction input for a CHECKLOCKTIMEVERIFY script.");
}
private static void executeCheckSequenceVerify(Transaction txContainingThis, int index, LinkedList<byte[]> stack) throws ScriptException {
private static void executeCheckSequenceVerify(Transaction txContainingThis, int index, LinkedList<byte[]> stack, Set<VerifyFlag> verifyFlags) throws ScriptException {
if (stack.size() < 1)
throw new ScriptException("Attempted OP_CHECKLOCKTIMEVERIFY on a stack with size < 1");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKSEQUENCEVERIFY on a stack with size < 1");
// Note that elsewhere numeric opcodes are limited to
// operands in the range -2**31+1 to 2**31-1, however it is
@ -1443,13 +1470,13 @@ public class Script {
// Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums, which are good until 2**39-1, well
// beyond the 2**32-1 limit of the nSequence field itself.
final long nSequence = castToBigInteger(stack.getLast(), 5).longValue();
final long nSequence = castToBigInteger(stack.getLast(), 5, verifyFlags.contains(VerifyFlag.MINIMALDATA)).longValue();
// In the rare event that the argument may be < 0 due to
// some arithmetic being done first, you can always use
// 0 MAX CHECKSEQUENCEVERIFY.
if (nSequence < 0)
throw new ScriptException("Negative sequence");
throw new ScriptException(ScriptError.SCRIPT_ERR_NEGATIVE_LOCKTIME, "Negative sequence");
// To provide for future soft-fork extensibility, if the
// operand has the disabled lock-time flag set,
@ -1459,7 +1486,7 @@ public class Script {
// Compare the specified sequence number with the input.
if (!checkSequence(nSequence, txContainingThis, index))
throw new ScriptException("Unsatisfied CHECKLOCKTIMEVERIFY lock time");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNSATISFIED_LOCKTIME, "Unsatisfied CHECKLOCKTIMEVERIFY lock time");
}
private static boolean checkSequence(long nSequence, Transaction txContainingThis, int index) {
@ -1512,7 +1539,7 @@ public class Script {
|| verifyFlags.contains(VerifyFlag.DERSIG)
|| verifyFlags.contains(VerifyFlag.LOW_S);
if (stack.size() < 2)
throw new ScriptException("Attempted OP_CHECKSIG(VERIFY) on a stack with size < 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKSIG(VERIFY) on a stack with size < 2");
byte[] pubKey = stack.pollLast();
byte[] sigBytes = stack.pollLast();
@ -1550,7 +1577,7 @@ public class Script {
stack.add(sigValid ? new byte[] {1} : new byte[] {});
else if (opcode == OP_CHECKSIGVERIFY)
if (!sigValid)
throw new ScriptException("Script failed OP_CHECKSIGVERIFY");
throw new ScriptException(ScriptError.SCRIPT_ERR_CHECKSIGVERIFY, "Script failed OP_CHECKSIGVERIFY");
}
private static int executeMultiSig(Transaction txContainingThis, int index, Script script, LinkedList<byte[]> stack,
@ -1559,16 +1586,16 @@ public class Script {
final boolean requireCanonical = verifyFlags.contains(VerifyFlag.STRICTENC)
|| verifyFlags.contains(VerifyFlag.DERSIG)
|| verifyFlags.contains(VerifyFlag.LOW_S);
if (stack.size() < 2)
throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2");
int pubKeyCount = castToBigInteger(stack.pollLast()).intValue();
if (stack.size() < 1)
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2");
int pubKeyCount = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA)).intValue();
if (pubKeyCount < 0 || pubKeyCount > 20)
throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with pubkey count out of range");
throw new ScriptException(ScriptError.SCRIPT_ERR_PUBKEY_COUNT, "OP_CHECKMULTISIG(VERIFY) with pubkey count out of range");
opCount += pubKeyCount;
if (opCount > 201)
throw new ScriptException("Total op count > 201 during OP_CHECKMULTISIG(VERIFY)");
throw new ScriptException(ScriptError.SCRIPT_ERR_OP_COUNT, "Total op count > 201 during OP_CHECKMULTISIG(VERIFY)");
if (stack.size() < pubKeyCount + 1)
throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + 2");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + 2");
LinkedList<byte[]> pubkeys = new LinkedList<>();
for (int i = 0; i < pubKeyCount; i++) {
@ -1576,11 +1603,11 @@ public class Script {
pubkeys.add(pubKey);
}
int sigCount = castToBigInteger(stack.pollLast()).intValue();
int sigCount = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA)).intValue();
if (sigCount < 0 || sigCount > pubKeyCount)
throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with sig count out of range");
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_COUNT, "OP_CHECKMULTISIG(VERIFY) with sig count out of range");
if (stack.size() < sigCount + 1)
throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + num_of_signatures + 3");
throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + num_of_signatures + 3");
LinkedList<byte[]> sigs = new LinkedList<>();
for (int i = 0; i < sigCount; i++) {
@ -1625,13 +1652,13 @@ public class Script {
// We uselessly remove a stack object to emulate a Bitcoin Core bug.
byte[] nullDummy = stack.pollLast();
if (verifyFlags.contains(VerifyFlag.NULLDUMMY) && nullDummy.length > 0)
throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with non-null nulldummy: " + Arrays.toString(nullDummy));
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_NULLFAIL, "OP_CHECKMULTISIG(VERIFY) with non-null nulldummy: " + Arrays.toString(nullDummy));
if (opcode == OP_CHECKMULTISIG) {
stack.add(valid ? new byte[] {1} : new byte[] {});
} else if (opcode == OP_CHECKMULTISIGVERIFY) {
if (!valid)
throw new ScriptException("Script failed OP_CHECKMULTISIGVERIFY");
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_NULLFAIL, "Script failed OP_CHECKMULTISIGVERIFY");
}
return opCount;
}
@ -1672,7 +1699,7 @@ public class Script {
throw new RuntimeException(e); // Should not happen unless we were given a totally broken transaction.
}
if (getProgram().length > 10000 || scriptPubKey.getProgram().length > 10000)
throw new ScriptException("Script larger than 10,000 bytes");
throw new ScriptException(ScriptError.SCRIPT_ERR_SCRIPT_SIZE, "Script larger than 10,000 bytes");
LinkedList<byte[]> stack = new LinkedList<>();
LinkedList<byte[]> p2shStack = null;
@ -1683,10 +1710,10 @@ public class Script {
executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack, verifyFlags);
if (stack.size() == 0)
throw new ScriptException("Stack empty at end of script execution.");
throw new ScriptException(ScriptError.SCRIPT_ERR_EVAL_FALSE, "Stack empty at end of script execution.");
if (!castToBool(stack.pollLast()))
throw new ScriptException("Script resulted in a non-true stack: " + stack);
throw new ScriptException(ScriptError.SCRIPT_ERR_EVAL_FALSE, "Script resulted in a non-true stack: " + stack);
// P2SH is pay to script hash. It means that the scriptPubKey has a special form which is a valid
// program but it has "useless" form that if evaluated as a normal program always returns true.
@ -1704,7 +1731,7 @@ public class Script {
if (verifyFlags.contains(VerifyFlag.P2SH) && scriptPubKey.isPayToScriptHash()) {
for (ScriptChunk chunk : chunks)
if (chunk.isOpCode() && chunk.opcode > OP_16)
throw new ScriptException("Attempted to spend a P2SH scriptPubKey with a script that contained script ops");
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_PUSHONLY, "Attempted to spend a P2SH scriptPubKey with a script that contained script ops");
byte[] scriptPubKeyBytes = p2shStack.pollLast();
Script scriptPubKeyP2SH = new Script(scriptPubKeyBytes);
@ -1712,10 +1739,10 @@ public class Script {
executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack, verifyFlags);
if (p2shStack.size() == 0)
throw new ScriptException("P2SH stack empty at end of script execution.");
throw new ScriptException(ScriptError.SCRIPT_ERR_EVAL_FALSE, "P2SH stack empty at end of script execution.");
if (!castToBool(p2shStack.pollLast()))
throw new ScriptException("P2SH script execution resulted in a non-true stack");
throw new ScriptException(ScriptError.SCRIPT_ERR_EVAL_FALSE, "P2SH script execution resulted in a non-true stack");
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2017 Nicola Atzei
*
* 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 org.bitcoinj.script;
import java.util.HashMap;
import java.util.Map;
public enum ScriptError {
SCRIPT_ERR_OK("OK"),
SCRIPT_ERR_UNKNOWN_ERROR("UNKNOWN_ERROR"),
SCRIPT_ERR_EVAL_FALSE("EVAL_FALSE"),
SCRIPT_ERR_OP_RETURN("OP_RETURN"),
/* Max sizes */
SCRIPT_ERR_SCRIPT_SIZE("SCRIPT_SIZE"),
SCRIPT_ERR_PUSH_SIZE("PUSH_SIZE"),
SCRIPT_ERR_OP_COUNT("OP_COUNT"),
SCRIPT_ERR_STACK_SIZE("STACK_SIZE"),
SCRIPT_ERR_SIG_COUNT("SIG_COUNT"),
SCRIPT_ERR_PUBKEY_COUNT("PUBKEY_COUNT"),
/* Failed verify operations */
SCRIPT_ERR_VERIFY("VERIFY"),
SCRIPT_ERR_EQUALVERIFY("EQUALVERIFY"),
SCRIPT_ERR_CHECKMULTISIGVERIFY("CHECKMULTISIGVERIFY"),
SCRIPT_ERR_CHECKSIGVERIFY("CHECKSIGVERIFY"),
SCRIPT_ERR_NUMEQUALVERIFY("NUMEQUALVERIFY"),
/* Logical/Format/Canonical errors */
SCRIPT_ERR_BAD_OPCODE("BAD_OPCODE"),
SCRIPT_ERR_DISABLED_OPCODE("DISABLED_OPCODE"),
SCRIPT_ERR_INVALID_STACK_OPERATION("INVALID_STACK_OPERATION"),
SCRIPT_ERR_INVALID_ALTSTACK_OPERATION("INVALID_ALTSTACK_OPERATION"),
SCRIPT_ERR_UNBALANCED_CONDITIONAL("UNBALANCED_CONDITIONAL"),
/* CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY */
SCRIPT_ERR_NEGATIVE_LOCKTIME("NEGATIVE_LOCKTIME"),
SCRIPT_ERR_UNSATISFIED_LOCKTIME("UNSATISFIED_LOCKTIME"),
/* Malleability */
SCRIPT_ERR_SIG_HASHTYPE("SIG_HASHTYPE"),
SCRIPT_ERR_SIG_DER("SIG_DER"),
SCRIPT_ERR_MINIMALDATA("MINIMALDATA"),
SCRIPT_ERR_SIG_PUSHONLY("SIG_PUSHONLY"),
SCRIPT_ERR_SIG_HIGH_S("SIG_HIGH_S"),
SCRIPT_ERR_SIG_NULLDUMMY("SIG_NULLDUMMY"),
SCRIPT_ERR_PUBKEYTYPE("PUBKEYTYPE"),
SCRIPT_ERR_CLEANSTACK("CLEANSTACK"),
SCRIPT_ERR_MINIMALIF("MINIMALIF"),
SCRIPT_ERR_SIG_NULLFAIL("NULLFAIL"),
/* softfork safeness */
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS("DISCOURAGE_UPGRADABLE_NOPS"),
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"),
/* segregated witness */
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH("WITNESS_PROGRAM_WRONG_LENGTH"),
SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY("WITNESS_PROGRAM_WITNESS_EMPTY"),
SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH("WITNESS_PROGRAM_MISMATCH"),
SCRIPT_ERR_WITNESS_MALLEATED("WITNESS_MALLEATED"),
SCRIPT_ERR_WITNESS_MALLEATED_P2SH("WITNESS_MALLEATED_P2SH"),
SCRIPT_ERR_WITNESS_UNEXPECTED("WITNESS_UNEXPECTED"),
SCRIPT_ERR_WITNESS_PUBKEYTYPE("WITNESS_PUBKEYTYPE"),
SCRIPT_ERR_ERROR_COUNT("ERROR_COUNT");
private final String mnemonic;
private static final Map<String, ScriptError> mnemonicToScriptErrorMap;
private ScriptError(String name) {
this.mnemonic = name;
}
static {
mnemonicToScriptErrorMap = new HashMap<>();
for (ScriptError err : ScriptError.values()) {
mnemonicToScriptErrorMap.put(err.getMnemonic(), err);
}
}
public String getMnemonic() {
return mnemonic;
}
public static ScriptError fromMnemonic(String name) {
ScriptError err = mnemonicToScriptErrorMap.get(name);
if (err == null)
throw new IllegalArgumentException(name + " is not a valid name");
return err;
}
}

View File

@ -299,7 +299,7 @@ public class TransactionTest {
TransactionInput ti = new TransactionInput(PARAMS, tx, new byte[0]) {
@Override
public Script getScriptSig() throws ScriptException {
throw new ScriptException("");
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "");
}
};

View File

@ -292,19 +292,19 @@ public class ScriptTest {
if (test.size() == 1)
continue; // skip comment
Set<VerifyFlag> verifyFlags = parseVerifyFlags(test.get(2).asText());
String expectedError = test.get(3).asText();
ScriptError expectedError = ScriptError.fromMnemonic(test.get(3).asText());
try {
Script scriptSig = parseScriptString(test.get(0).asText());
Script scriptPubKey = parseScriptString(test.get(1).asText());
Transaction txCredit = buildCreditingTransaction(scriptPubKey);
Transaction txSpend = buildSpendingTransaction(txCredit, scriptSig);
scriptSig.correctlySpends(txSpend, 0, scriptPubKey, verifyFlags);
if (!expectedError.equals("OK"))
if (!expectedError.equals(ScriptError.SCRIPT_ERR_OK))
fail(test + " is expected to fail");
} catch (ScriptException e) {
if (expectedError.equals("OK")) {
// TODO check for specific error
if (!e.getError().equals(expectedError)) {
System.err.println(test);
e.printStackTrace();
System.err.flush();
throw e;
}

View File

@ -1121,6 +1121,103 @@
["0x00", "'00' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE", "Basic OP_0 execution"],
["MINIMALDATA enforcement for PUSHDATAs"],
["0x4c 0x00", "DROP 1", "MINIMALDATA", "MINIMALDATA", "Empty vector minimally represented by OP_0"],
["0x01 0x81", "DROP 1", "MINIMALDATA", "MINIMALDATA", "-1 minimally represented by OP_1NEGATE"],
["0x01 0x01", "DROP 1", "MINIMALDATA", "MINIMALDATA", "1 to 16 minimally represented by OP_1 to OP_16"],
["0x01 0x02", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x03", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x04", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x05", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x06", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x07", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x08", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x09", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x0a", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x0b", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x0c", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x0d", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x0e", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x0f", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x01 0x10", "DROP 1", "MINIMALDATA", "MINIMALDATA"],
["0x4c 0x48 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA",
"MINIMALDATA",
"PUSHDATA1 of 72 bytes minimally represented by direct push"],
["0x4d 0xFF00 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA",
"MINIMALDATA",
"PUSHDATA2 of 255 bytes minimally represented by PUSHDATA1"],
["0x4e 0x00010000 0x11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA",
"MINIMALDATA",
"PUSHDATA4 of 256 bytes minimally represented by PUSHDATA2"],
["MINIMALDATA enforcement for numeric arguments"],
["0x0000", "DROP 0 EQUAL", "MINIMALDATA,CLEANSTACK", "OK", "numequals 0"],
["0x01 0x00", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals 0"],
["0x02 0x0000", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals 0"],
["0x01 0x80", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "0x80 (negative zero) numequals 0"],
["0x02 0x0080", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals 0"],
["0x02 0x0500", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals 5"],
["0x03 0x050000", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals 5"],
["0x02 0x0580", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals -5"],
["0x03 0x050080", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "numequals -5"],
["0x03 0xff7f80", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "Minimal encoding is 0xffff"],
["0x03 0xff7f00", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "Minimal encoding is 0xff7f"],
["0x04 0xffff7f80", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "Minimal encoding is 0xffffff"],
["0x04 0xffff7f00", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR", "Minimal encoding is 0xffff7f"],
["Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule"],
["1 0x02 0x0000", "PICK DROP", "MINIMALDATA", "UNKNOWN_ERROR"],
["1 0x02 0x0000", "ROLL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000", "1ADD DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000", "1SUB DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000", "NEGATE DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000", "ABS DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000", "NOT DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000", "0NOTEQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "ADD DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "ADD DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "SUB DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "SUB DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "BOOLAND DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "BOOLAND DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "BOOLOR DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "BOOLOR DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "NUMEQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 1", "NUMEQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "NUMEQUALVERIFY 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "NUMEQUALVERIFY 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "NUMNOTEQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "NUMNOTEQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "LESSTHAN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "LESSTHAN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "GREATERTHAN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "GREATERTHAN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "LESSTHANOREQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "LESSTHANOREQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "GREATERTHANOREQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "GREATERTHANOREQUAL DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "MIN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "MIN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000", "MAX DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0", "MAX DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0x02 0x0000 0 0", "WITHIN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000 0", "WITHIN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0 0x02 0x0000", "WITHIN DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0 0x02 0x0000", "CHECKMULTISIG DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000 0", "CHECKMULTISIG DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000 0 1", "CHECKMULTISIG DROP 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0 0x02 0x0000", "CHECKMULTISIGVERIFY 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", "MINIMALDATA", "UNKNOWN_ERROR"],
["CHECKSEQUENCEVERIFY tests"],
["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on an empty stack"],
["-1", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "NEGATIVE_LOCKTIME", "CSV automatically fails if stack top is negative"],