3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-13 10:45:51 +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; package org.bitcoinj.core;
import org.bitcoinj.script.ScriptError;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class ScriptException extends VerificationException { public class ScriptException extends VerificationException {
public ScriptException(String msg) { private final ScriptError err;
public ScriptException(ScriptError err, String msg) {
super(msg); super(msg);
this.err = err;
} }
public ScriptException(String msg, Exception e) { public ScriptException(ScriptError err, String msg, Exception e) {
super(msg, 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.crypto.TransactionSignature;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptError;
import org.bitcoinj.script.ScriptOpCodes; import org.bitcoinj.script.ScriptOpCodes;
import org.bitcoinj.signers.TransactionSigner; import org.bitcoinj.signers.TransactionSigner;
import org.bitcoinj.utils.ExchangeRate; import org.bitcoinj.utils.ExchangeRate;
@ -827,7 +828,7 @@ public class Transaction extends ChildMessage {
else if (scriptPubKey.isSentToAddress()) else if (scriptPubKey.isSentToAddress())
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
else 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; return input;
} }

View File

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

View File

@ -148,7 +148,7 @@ public class TransactionOutPoint extends ChildMessage {
byte[] pubkeyBytes = connectedScript.getPubKey(); byte[] pubkeyBytes = connectedScript.getPubKey();
return keyBag.findKeyFromPubKey(pubkeyBytes); return keyBag.findKeyFromPubKey(pubkeyBytes);
} else { } 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(); byte[] scriptHash = connectedScript.getPubKeyHash();
return keyBag.findRedeemDataFromScriptHash(scriptHash); return keyBag.findRedeemDataFromScriptHash(scriptHash);
} else { } 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 2011 Google Inc.
* Copyright 2012 Matt Corallo. * Copyright 2012 Matt Corallo.
* Copyright 2014 Andreas Schildbach * Copyright 2014 Andreas Schildbach
* Copyright 2017 Nicola Atzei
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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. // Read some bytes of data, where how many is the opcode value itself.
dataToRead = opcode; dataToRead = opcode;
} else if (opcode == OP_PUSHDATA1) { } 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(); dataToRead = bis.read();
} else if (opcode == OP_PUSHDATA2) { } else if (opcode == OP_PUSHDATA2) {
// Read a short, then read that many bytes of data. // 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); dataToRead = bis.read() | (bis.read() << 8);
} else if (opcode == OP_PUSHDATA4) { } else if (opcode == OP_PUSHDATA4) {
// Read a uint32, then read that many bytes of data. // 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 // 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); 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); chunk = new ScriptChunk(opcode, null, startLocationInProgram);
} else { } else {
if (dataToRead > bis.available()) 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]; byte[] data = new byte[(int)dataToRead];
checkState(dataToRead == 0 || bis.read(data, 0, (int)dataToRead) == dataToRead); checkState(dataToRead == 0 || bis.read(data, 0, (int)dataToRead) == dataToRead);
chunk = new ScriptChunk(opcode, data, startLocationInProgram); chunk = new ScriptChunk(opcode, data, startLocationInProgram);
@ -273,7 +274,7 @@ public class Script {
else if (isPayToScriptHash()) else if (isPayToScriptHash())
return chunks.get(1).data; return chunks.get(1).data;
else 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 { public byte[] getPubKey() throws ScriptException {
if (chunks.size() != 2) { 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 ScriptChunk chunk0 = chunks.get(0);
final byte[] chunk0data = chunk0.data; final byte[] chunk0data = chunk0.data;
@ -299,7 +300,7 @@ public class Script {
// A large constant followed by an OP_CHECKSIG is the key. // A large constant followed by an OP_CHECKSIG is the key.
return chunk0data; return chunk0data;
} else { } 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 { public byte[] getCLTVPaymentChannelSenderPubKey() throws ScriptException {
if (!isSentToCLTVPaymentChannel()) { 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; return chunks.get(8).data;
} }
@ -322,16 +323,16 @@ public class Script {
*/ */
public byte[] getCLTVPaymentChannelRecipientPubKey() throws ScriptException { public byte[] getCLTVPaymentChannelRecipientPubKey() throws ScriptException {
if (!isSentToCLTVPaymentChannel()) { 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; return chunks.get(1).data;
} }
public BigInteger getCLTVPaymentChannelExpiry() { public BigInteger getCLTVPaymentChannelExpiry() {
if (!isSentToCLTVPaymentChannel()) { 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()) else if (forcePayToPubKey && isSentToRawPubKey())
return ECKey.fromPublicOnly(getPubKey()).toAddress(params); return ECKey.fromPublicOnly(getPubKey()).toAddress(params);
else 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 //////////////////////////////// ////////////////////// Interface for writing scripts from scratch ////////////////////////////////
@ -454,7 +455,7 @@ public class Script {
checkArgument(redeemScript != null, "Redeem script required to create P2SH input script"); checkArgument(redeemScript != null, "Redeem script required to create P2SH input script");
return ScriptBuilder.createP2SHMultiSigInputScript(null, redeemScript); return ScriptBuilder.createP2SHMultiSigInputScript(null, redeemScript);
} else { } 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() { public List<ECKey> getPubKeys() {
if (!isSentToMultiSig()) 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(); ArrayList<ECKey> result = Lists.newArrayList();
int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode); int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode);
@ -808,10 +809,8 @@ public class Script {
* sizes. * sizes.
* @throws ScriptException if the chunk is longer than 4 bytes. * @throws ScriptException if the chunk is longer than 4 bytes.
*/ */
private static BigInteger castToBigInteger(byte[] chunk) throws ScriptException { private static BigInteger castToBigInteger(byte[] chunk, final boolean requireMinimal) throws ScriptException {
if (chunk.length > 4) return castToBigInteger(chunk, 4, requireMinimal);
throw new ScriptException("Script attempted to use an integer larger than 4 bytes");
return Utils.decodeMPI(Utils.reverseBytes(chunk), false);
} }
/** /**
@ -820,12 +819,32 @@ public class Script {
* the normal maximum length does not apply (i.e. CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY). * the normal maximum length does not apply (i.e. CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY).
* *
* @param maxLength the maximum length in bytes. * @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. * @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) if (chunk.length > maxLength)
throw new ScriptException("Script attempted to use an integer larger than " throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script attempted to use an integer larger than " + maxLength + " bytes");
+ 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); return Utils.decodeMPI(Utils.reverseBytes(chunk), false);
} }
@ -875,31 +894,39 @@ public class Script {
if (!shouldExecute) if (!shouldExecute)
continue; 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[] {}); stack.add(new byte[] {});
} else if (!chunk.isOpCode()) { } else if (!chunk.isOpCode()) {
if (chunk.data.length > MAX_SCRIPT_ELEMENT_SIZE) 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) if (!shouldExecute)
continue; 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); stack.add(chunk.data);
} else { } else {
int opcode = chunk.opcode; int opcode = chunk.opcode;
if (opcode > OP_16) { if (opcode > OP_16) {
opCount++; opCount++;
if (opCount > 201) 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) 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 || 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_INVERT || opcode == OP_AND || opcode == OP_OR || opcode == OP_XOR ||
opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_DIV || opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_DIV ||
opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) 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) { switch (opcode) {
case OP_IF: case OP_IF:
@ -908,7 +935,7 @@ public class Script {
continue; continue;
} }
if (stack.size() < 1) 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())); ifStack.add(castToBool(stack.pollLast()));
continue; continue;
case OP_NOTIF: case OP_NOTIF:
@ -917,17 +944,17 @@ public class Script {
continue; continue;
} }
if (stack.size() < 1) 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())); ifStack.add(!castToBool(stack.pollLast()));
continue; continue;
case OP_ELSE: case OP_ELSE:
if (ifStack.isEmpty()) 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()); ifStack.add(!ifStack.pollLast());
continue; continue;
case OP_ENDIF: case OP_ENDIF:
if (ifStack.isEmpty()) 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(); ifStack.pollLast();
continue; continue;
} }
@ -962,31 +989,31 @@ public class Script {
break; break;
case OP_VERIFY: case OP_VERIFY:
if (stack.size() < 1) 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())) if (!castToBool(stack.pollLast()))
throw new ScriptException("OP_VERIFY failed"); throw new ScriptException(ScriptError.SCRIPT_ERR_VERIFY, "OP_VERIFY failed");
break; break;
case OP_RETURN: 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: case OP_TOALTSTACK:
if (stack.size() < 1) 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()); altstack.add(stack.pollLast());
break; break;
case OP_FROMALTSTACK: case OP_FROMALTSTACK:
if (altstack.size() < 1) 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()); stack.add(altstack.pollLast());
break; break;
case OP_2DROP: case OP_2DROP:
if (stack.size() < 2) 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();
stack.pollLast(); stack.pollLast();
break; break;
case OP_2DUP: case OP_2DUP:
if (stack.size() < 2) 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(); Iterator<byte[]> it2DUP = stack.descendingIterator();
byte[] OP2DUPtmpChunk2 = it2DUP.next(); byte[] OP2DUPtmpChunk2 = it2DUP.next();
stack.add(it2DUP.next()); stack.add(it2DUP.next());
@ -994,7 +1021,7 @@ public class Script {
break; break;
case OP_3DUP: case OP_3DUP:
if (stack.size() < 3) 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(); Iterator<byte[]> it3DUP = stack.descendingIterator();
byte[] OP3DUPtmpChunk3 = it3DUP.next(); byte[] OP3DUPtmpChunk3 = it3DUP.next();
byte[] OP3DUPtmpChunk2 = it3DUP.next(); byte[] OP3DUPtmpChunk2 = it3DUP.next();
@ -1004,7 +1031,7 @@ public class Script {
break; break;
case OP_2OVER: case OP_2OVER:
if (stack.size() < 4) 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(); Iterator<byte[]> it2OVER = stack.descendingIterator();
it2OVER.next(); it2OVER.next();
it2OVER.next(); it2OVER.next();
@ -1014,7 +1041,7 @@ public class Script {
break; break;
case OP_2ROT: case OP_2ROT:
if (stack.size() < 6) 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[] OP2ROTtmpChunk6 = stack.pollLast();
byte[] OP2ROTtmpChunk5 = stack.pollLast(); byte[] OP2ROTtmpChunk5 = stack.pollLast();
byte[] OP2ROTtmpChunk4 = stack.pollLast(); byte[] OP2ROTtmpChunk4 = stack.pollLast();
@ -1030,7 +1057,7 @@ public class Script {
break; break;
case OP_2SWAP: case OP_2SWAP:
if (stack.size() < 4) 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[] OP2SWAPtmpChunk4 = stack.pollLast();
byte[] OP2SWAPtmpChunk3 = stack.pollLast(); byte[] OP2SWAPtmpChunk3 = stack.pollLast();
byte[] OP2SWAPtmpChunk2 = stack.pollLast(); byte[] OP2SWAPtmpChunk2 = stack.pollLast();
@ -1042,7 +1069,7 @@ public class Script {
break; break;
case OP_IFDUP: case OP_IFDUP:
if (stack.size() < 1) 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())) if (castToBool(stack.getLast()))
stack.add(stack.getLast()); stack.add(stack.getLast());
break; break;
@ -1051,24 +1078,24 @@ public class Script {
break; break;
case OP_DROP: case OP_DROP:
if (stack.size() < 1) 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(); stack.pollLast();
break; break;
case OP_DUP: case OP_DUP:
if (stack.size() < 1) 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()); stack.add(stack.getLast());
break; break;
case OP_NIP: case OP_NIP:
if (stack.size() < 2) 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(); byte[] OPNIPtmpChunk = stack.pollLast();
stack.pollLast(); stack.pollLast();
stack.add(OPNIPtmpChunk); stack.add(OPNIPtmpChunk);
break; break;
case OP_OVER: case OP_OVER:
if (stack.size() < 2) 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(); Iterator<byte[]> itOVER = stack.descendingIterator();
itOVER.next(); itOVER.next();
stack.add(itOVER.next()); stack.add(itOVER.next());
@ -1076,10 +1103,10 @@ public class Script {
case OP_PICK: case OP_PICK:
case OP_ROLL: case OP_ROLL:
if (stack.size() < 1) if (stack.size() < 1)
throw new ScriptException("Attempted OP_PICK/OP_ROLL on an empty stack"); throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_PICK/OP_ROLL on an empty stack");
long val = castToBigInteger(stack.pollLast()).longValue(); long val = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA)).longValue();
if (val < 0 || val >= stack.size()) 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(); Iterator<byte[]> itPICK = stack.descendingIterator();
for (long i = 0; i < val; i++) for (long i = 0; i < val; i++)
itPICK.next(); itPICK.next();
@ -1090,7 +1117,7 @@ public class Script {
break; break;
case OP_ROT: case OP_ROT:
if (stack.size() < 3) 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[] OPROTtmpChunk3 = stack.pollLast();
byte[] OPROTtmpChunk2 = stack.pollLast(); byte[] OPROTtmpChunk2 = stack.pollLast();
byte[] OPROTtmpChunk1 = stack.pollLast(); byte[] OPROTtmpChunk1 = stack.pollLast();
@ -1101,7 +1128,7 @@ public class Script {
case OP_SWAP: case OP_SWAP:
case OP_TUCK: case OP_TUCK:
if (stack.size() < 2) 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[] OPSWAPtmpChunk2 = stack.pollLast();
byte[] OPSWAPtmpChunk1 = stack.pollLast(); byte[] OPSWAPtmpChunk1 = stack.pollLast();
stack.add(OPSWAPtmpChunk2); stack.add(OPSWAPtmpChunk2);
@ -1113,27 +1140,27 @@ public class Script {
case OP_SUBSTR: case OP_SUBSTR:
case OP_LEFT: case OP_LEFT:
case OP_RIGHT: 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: case OP_SIZE:
if (stack.size() < 1) 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))); stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(stack.getLast().length), false)));
break; break;
case OP_INVERT: case OP_INVERT:
case OP_AND: case OP_AND:
case OP_OR: case OP_OR:
case OP_XOR: 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: case OP_EQUAL:
if (stack.size() < 2) 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[] {}); stack.add(Arrays.equals(stack.pollLast(), stack.pollLast()) ? new byte[] {1} : new byte[] {});
break; break;
case OP_EQUALVERIFY: case OP_EQUALVERIFY:
if (stack.size() < 2) 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())) 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; break;
case OP_1ADD: case OP_1ADD:
case OP_1SUB: case OP_1SUB:
@ -1142,8 +1169,8 @@ public class Script {
case OP_NOT: case OP_NOT:
case OP_0NOTEQUAL: case OP_0NOTEQUAL:
if (stack.size() < 1) if (stack.size() < 1)
throw new ScriptException("Attempted a numeric op on an empty stack"); throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted a numeric op on an empty stack");
BigInteger numericOPnum = castToBigInteger(stack.pollLast()); BigInteger numericOPnum = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
switch (opcode) { switch (opcode) {
case OP_1ADD: case OP_1ADD:
@ -1179,7 +1206,7 @@ public class Script {
break; break;
case OP_2MUL: case OP_2MUL:
case OP_2DIV: 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_ADD:
case OP_SUB: case OP_SUB:
case OP_BOOLAND: case OP_BOOLAND:
@ -1193,9 +1220,9 @@ public class Script {
case OP_MIN: case OP_MIN:
case OP_MAX: case OP_MAX:
if (stack.size() < 2) if (stack.size() < 2)
throw new ScriptException("Attempted a numeric op on a stack with size < 2"); throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted a numeric op on a stack with size < 2");
BigInteger numericOPnum2 = castToBigInteger(stack.pollLast()); BigInteger numericOPnum2 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger numericOPnum1 = castToBigInteger(stack.pollLast()); BigInteger numericOPnum1 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger numericOPresult; BigInteger numericOPresult;
switch (opcode) { switch (opcode) {
@ -1276,22 +1303,22 @@ public class Script {
case OP_MOD: case OP_MOD:
case OP_LSHIFT: case OP_LSHIFT:
case OP_RSHIFT: 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: case OP_NUMEQUALVERIFY:
if (stack.size() < 2) if (stack.size() < 2)
throw new ScriptException("Attempted OP_NUMEQUALVERIFY on a stack with size < 2"); throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_NUMEQUALVERIFY on a stack with size < 2");
BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger(stack.pollLast()); BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger(stack.pollLast()); BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
if (!OPNUMEQUALVERIFYnum1.equals(OPNUMEQUALVERIFYnum2)) if (!OPNUMEQUALVERIFYnum1.equals(OPNUMEQUALVERIFYnum2))
throw new ScriptException("OP_NUMEQUALVERIFY failed"); throw new ScriptException(ScriptError.SCRIPT_ERR_NUMEQUALVERIFY, "OP_NUMEQUALVERIFY failed");
break; break;
case OP_WITHIN: case OP_WITHIN:
if (stack.size() < 3) if (stack.size() < 3)
throw new ScriptException("Attempted OP_WITHIN on a stack with size < 3"); throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_WITHIN on a stack with size < 3");
BigInteger OPWITHINnum3 = castToBigInteger(stack.pollLast()); BigInteger OPWITHINnum3 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger OPWITHINnum2 = castToBigInteger(stack.pollLast()); BigInteger OPWITHINnum2 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
BigInteger OPWITHINnum1 = castToBigInteger(stack.pollLast()); BigInteger OPWITHINnum1 = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA));
if (OPWITHINnum2.compareTo(OPWITHINnum1) <= 0 && OPWITHINnum1.compareTo(OPWITHINnum3) < 0) if (OPWITHINnum2.compareTo(OPWITHINnum1) <= 0 && OPWITHINnum1.compareTo(OPWITHINnum3) < 0)
stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ONE, false))); stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ONE, false)));
else else
@ -1299,7 +1326,7 @@ public class Script {
break; break;
case OP_RIPEMD160: case OP_RIPEMD160:
if (stack.size() < 1) 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(); RIPEMD160Digest digest = new RIPEMD160Digest();
byte[] dataToHash = stack.pollLast(); byte[] dataToHash = stack.pollLast();
digest.update(dataToHash, 0, dataToHash.length); digest.update(dataToHash, 0, dataToHash.length);
@ -1309,7 +1336,7 @@ public class Script {
break; break;
case OP_SHA1: case OP_SHA1:
if (stack.size() < 1) 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 { try {
stack.add(MessageDigest.getInstance("SHA-1").digest(stack.pollLast())); stack.add(MessageDigest.getInstance("SHA-1").digest(stack.pollLast()));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
@ -1318,17 +1345,17 @@ public class Script {
break; break;
case OP_SHA256: case OP_SHA256:
if (stack.size() < 1) 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())); stack.add(Sha256Hash.hash(stack.pollLast()));
break; break;
case OP_HASH160: case OP_HASH160:
if (stack.size() < 1) 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())); stack.add(Utils.sha256hash160(stack.pollLast()));
break; break;
case OP_HASH256: case OP_HASH256:
if (stack.size() < 1) 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())); stack.add(Sha256Hash.hashTwice(stack.pollLast()));
break; break;
case OP_CODESEPARATOR: case OP_CODESEPARATOR:
@ -1350,21 +1377,21 @@ public class Script {
if (!verifyFlags.contains(VerifyFlag.CHECKLOCKTIMEVERIFY)) { if (!verifyFlags.contains(VerifyFlag.CHECKLOCKTIMEVERIFY)) {
// not enabled; treat as a NOP2 // not enabled; treat as a NOP2
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) { 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; break;
} }
executeCheckLockTimeVerify(txContainingThis, (int) index, stack); executeCheckLockTimeVerify(txContainingThis, (int) index, stack, verifyFlags);
break; break;
case OP_CHECKSEQUENCEVERIFY: case OP_CHECKSEQUENCEVERIFY:
if (!verifyFlags.contains(VerifyFlag.CHECKSEQUENCEVERIFY)) { if (!verifyFlags.contains(VerifyFlag.CHECKSEQUENCEVERIFY)) {
// not enabled; treat as a NOP3 // not enabled; treat as a NOP3
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) { 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; break;
} }
executeCheckSequenceVerify(txContainingThis, (int) index, stack); executeCheckSequenceVerify(txContainingThis, (int) index, stack, verifyFlags);
break; break;
case OP_NOP1: case OP_NOP1:
case OP_NOP4: case OP_NOP4:
@ -1375,46 +1402,46 @@ public class Script {
case OP_NOP9: case OP_NOP9:
case OP_NOP10: case OP_NOP10:
if (verifyFlags.contains(VerifyFlag.DISCOURAGE_UPGRADABLE_NOPS)) { 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; break;
default: 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) 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()) 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 // 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) 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 // Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums to avoid year 2038 issue. // 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) 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 // There are two kinds of nLockTime, need to ensure we're comparing apples-to-apples
if (!( 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) ||
((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 // Now that we know we're comparing apples-to-apples, the
// comparison is a simple numeric one. // comparison is a simple numeric one.
if (nLockTime.compareTo(BigInteger.valueOf(txContainingThis.getLockTime())) > 0) 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 // Finally the nLockTime feature can be disabled and thus
// CHECKLOCKTIMEVERIFY bypassed if every txin has been // CHECKLOCKTIMEVERIFY bypassed if every txin has been
@ -1427,12 +1454,12 @@ public class Script {
// inputs, but testing just this input minimizes the data // inputs, but testing just this input minimizes the data
// required to prove correct CHECKLOCKTIMEVERIFY execution. // required to prove correct CHECKLOCKTIMEVERIFY execution.
if (!txContainingThis.getInput(index).hasSequence()) 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) 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 // Note that elsewhere numeric opcodes are limited to
// operands in the range -2**31+1 to 2**31-1, however it is // 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 // Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums, which are good until 2**39-1, well // to 5-byte bignums, which are good until 2**39-1, well
// beyond the 2**32-1 limit of the nSequence field itself. // 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 // In the rare event that the argument may be < 0 due to
// some arithmetic being done first, you can always use // some arithmetic being done first, you can always use
// 0 MAX CHECKSEQUENCEVERIFY. // 0 MAX CHECKSEQUENCEVERIFY.
if (nSequence < 0) 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 // To provide for future soft-fork extensibility, if the
// operand has the disabled lock-time flag set, // operand has the disabled lock-time flag set,
@ -1459,8 +1486,8 @@ public class Script {
// Compare the specified sequence number with the input. // Compare the specified sequence number with the input.
if (!checkSequence(nSequence, txContainingThis, index)) 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) { private static boolean checkSequence(long nSequence, Transaction txContainingThis, int index) {
// Relative lock times are supported by comparing the passed // Relative lock times are supported by comparing the passed
@ -1512,7 +1539,7 @@ public class Script {
|| verifyFlags.contains(VerifyFlag.DERSIG) || verifyFlags.contains(VerifyFlag.DERSIG)
|| verifyFlags.contains(VerifyFlag.LOW_S); || verifyFlags.contains(VerifyFlag.LOW_S);
if (stack.size() < 2) 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[] pubKey = stack.pollLast();
byte[] sigBytes = stack.pollLast(); byte[] sigBytes = stack.pollLast();
@ -1550,7 +1577,7 @@ public class Script {
stack.add(sigValid ? new byte[] {1} : new byte[] {}); stack.add(sigValid ? new byte[] {1} : new byte[] {});
else if (opcode == OP_CHECKSIGVERIFY) else if (opcode == OP_CHECKSIGVERIFY)
if (!sigValid) 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, 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) final boolean requireCanonical = verifyFlags.contains(VerifyFlag.STRICTENC)
|| verifyFlags.contains(VerifyFlag.DERSIG) || verifyFlags.contains(VerifyFlag.DERSIG)
|| verifyFlags.contains(VerifyFlag.LOW_S); || verifyFlags.contains(VerifyFlag.LOW_S);
if (stack.size() < 2) if (stack.size() < 1)
throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2"); throw new ScriptException(ScriptError.SCRIPT_ERR_INVALID_STACK_OPERATION, "Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2");
int pubKeyCount = castToBigInteger(stack.pollLast()).intValue(); int pubKeyCount = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA)).intValue();
if (pubKeyCount < 0 || pubKeyCount > 20) 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; opCount += pubKeyCount;
if (opCount > 201) 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) 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<>(); LinkedList<byte[]> pubkeys = new LinkedList<>();
for (int i = 0; i < pubKeyCount; i++) { for (int i = 0; i < pubKeyCount; i++) {
@ -1576,11 +1603,11 @@ public class Script {
pubkeys.add(pubKey); pubkeys.add(pubKey);
} }
int sigCount = castToBigInteger(stack.pollLast()).intValue(); int sigCount = castToBigInteger(stack.pollLast(), verifyFlags.contains(VerifyFlag.MINIMALDATA)).intValue();
if (sigCount < 0 || sigCount > pubKeyCount) 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) 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<>(); LinkedList<byte[]> sigs = new LinkedList<>();
for (int i = 0; i < sigCount; i++) { 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. // We uselessly remove a stack object to emulate a Bitcoin Core bug.
byte[] nullDummy = stack.pollLast(); byte[] nullDummy = stack.pollLast();
if (verifyFlags.contains(VerifyFlag.NULLDUMMY) && nullDummy.length > 0) 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) { if (opcode == OP_CHECKMULTISIG) {
stack.add(valid ? new byte[] {1} : new byte[] {}); stack.add(valid ? new byte[] {1} : new byte[] {});
} else if (opcode == OP_CHECKMULTISIGVERIFY) { } else if (opcode == OP_CHECKMULTISIGVERIFY) {
if (!valid) if (!valid)
throw new ScriptException("Script failed OP_CHECKMULTISIGVERIFY"); throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_NULLFAIL, "Script failed OP_CHECKMULTISIGVERIFY");
} }
return opCount; return opCount;
} }
@ -1672,7 +1699,7 @@ public class Script {
throw new RuntimeException(e); // Should not happen unless we were given a totally broken transaction. throw new RuntimeException(e); // Should not happen unless we were given a totally broken transaction.
} }
if (getProgram().length > 10000 || scriptPubKey.getProgram().length > 10000) 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[]> stack = new LinkedList<>();
LinkedList<byte[]> p2shStack = null; LinkedList<byte[]> p2shStack = null;
@ -1683,10 +1710,10 @@ public class Script {
executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack, verifyFlags); executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack, verifyFlags);
if (stack.size() == 0) 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())) 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 // 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. // 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()) { if (verifyFlags.contains(VerifyFlag.P2SH) && scriptPubKey.isPayToScriptHash()) {
for (ScriptChunk chunk : chunks) for (ScriptChunk chunk : chunks)
if (chunk.isOpCode() && chunk.opcode > OP_16) 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(); byte[] scriptPubKeyBytes = p2shStack.pollLast();
Script scriptPubKeyP2SH = new Script(scriptPubKeyBytes); Script scriptPubKeyP2SH = new Script(scriptPubKeyBytes);
@ -1712,10 +1739,10 @@ public class Script {
executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack, verifyFlags); executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack, verifyFlags);
if (p2shStack.size() == 0) 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())) 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]) { TransactionInput ti = new TransactionInput(PARAMS, tx, new byte[0]) {
@Override @Override
public Script getScriptSig() throws ScriptException { 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) if (test.size() == 1)
continue; // skip comment continue; // skip comment
Set<VerifyFlag> verifyFlags = parseVerifyFlags(test.get(2).asText()); Set<VerifyFlag> verifyFlags = parseVerifyFlags(test.get(2).asText());
String expectedError = test.get(3).asText(); ScriptError expectedError = ScriptError.fromMnemonic(test.get(3).asText());
try { try {
Script scriptSig = parseScriptString(test.get(0).asText()); Script scriptSig = parseScriptString(test.get(0).asText());
Script scriptPubKey = parseScriptString(test.get(1).asText()); Script scriptPubKey = parseScriptString(test.get(1).asText());
Transaction txCredit = buildCreditingTransaction(scriptPubKey); Transaction txCredit = buildCreditingTransaction(scriptPubKey);
Transaction txSpend = buildSpendingTransaction(txCredit, scriptSig); Transaction txSpend = buildSpendingTransaction(txCredit, scriptSig);
scriptSig.correctlySpends(txSpend, 0, scriptPubKey, verifyFlags); scriptSig.correctlySpends(txSpend, 0, scriptPubKey, verifyFlags);
if (!expectedError.equals("OK")) if (!expectedError.equals(ScriptError.SCRIPT_ERR_OK))
fail(test + " is expected to fail"); fail(test + " is expected to fail");
} catch (ScriptException e) { } catch (ScriptException e) {
if (expectedError.equals("OK")) { if (!e.getError().equals(expectedError)) {
// TODO check for specific error
System.err.println(test); System.err.println(test);
e.printStackTrace();
System.err.flush(); System.err.flush();
throw e; throw e;
} }

View File

@ -1121,6 +1121,103 @@
["0x00", "'00' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE", "Basic OP_0 execution"], ["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 tests"],
["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on an empty stack"], ["", "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"], ["-1", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "NEGATIVE_LOCKTIME", "CSV automatically fails if stack top is negative"],