aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/bitcoinconsensus.cpp91
-rw-r--r--src/script/bitcoinconsensus.h67
-rw-r--r--src/script/interpreter.cpp371
-rw-r--r--src/script/interpreter.h23
-rw-r--r--src/script/script.h44
-rw-r--r--src/script/script_error.cpp71
-rw-r--r--src/script/script_error.h57
-rw-r--r--src/script/sigcache.cpp10
-rw-r--r--src/script/sign.cpp20
-rw-r--r--src/script/sign.h8
-rw-r--r--src/script/standard.cpp6
-rw-r--r--src/script/standard.h34
12 files changed, 599 insertions, 203 deletions
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
new file mode 100644
index 0000000000..4faa760ad7
--- /dev/null
+++ b/src/script/bitcoinconsensus.cpp
@@ -0,0 +1,91 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bitcoinconsensus.h"
+
+#include "core/transaction.h"
+#include "script/interpreter.h"
+#include "version.h"
+
+namespace {
+
+/** A class that deserializes a single CTransaction one time. */
+class TxInputStream
+{
+public:
+ TxInputStream(int nTypeIn, int nVersionIn, const unsigned char *txTo, size_t txToLen) :
+ m_type(nTypeIn),
+ m_version(nVersionIn),
+ m_data(txTo),
+ m_remaining(txToLen)
+ {}
+
+ TxInputStream& read(char* pch, size_t nSize)
+ {
+ if (nSize > m_remaining)
+ throw std::ios_base::failure(std::string(__func__) + ": end of data");
+
+ if (pch == NULL)
+ throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer");
+
+ if (m_data == NULL)
+ throw std::ios_base::failure(std::string(__func__) + ": bad source buffer");
+
+ memcpy(pch, m_data, nSize);
+ m_remaining -= nSize;
+ m_data += nSize;
+ return *this;
+ }
+
+ template<typename T>
+ TxInputStream& operator>>(T& obj)
+ {
+ ::Unserialize(*this, obj, m_type, m_version);
+ return *this;
+ }
+
+private:
+ const int m_type;
+ const int m_version;
+ const unsigned char* m_data;
+ size_t m_remaining;
+};
+
+inline int set_error(bitcoinconsensus_error* ret, bitcoinconsensus_error serror)
+{
+ if (ret)
+ *ret = serror;
+ return 0;
+}
+
+} // anon namespace
+
+int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
+ const unsigned char *txTo , unsigned int txToLen,
+ unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
+{
+ try {
+ TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
+ CTransaction tx;
+ stream >> tx;
+ if (nIn >= tx.vin.size())
+ return set_error(err, bitcoinconsensus_ERR_TX_INDEX);
+ if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen)
+ return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
+
+ // Regardless of the verification result, the tx did not error.
+ set_error(err, bitcoinconsensus_ERR_OK);
+
+ return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, SignatureChecker(tx, nIn), NULL);
+ } catch (std::exception &e) {
+ return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
+ }
+}
+
+unsigned int bitcoinconsensus_version()
+{
+ // Just use the API version for now
+ return BITCOINCONSENSUS_API_VER;
+}
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
new file mode 100644
index 0000000000..15e3337a8d
--- /dev/null
+++ b/src/script/bitcoinconsensus.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_BITCOINCONSENSUS_H
+#define BITCOIN_BITCOINCONSENSUS_H
+
+#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+ #if defined(_WIN32)
+ #if defined(DLL_EXPORT)
+ #if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT)
+ #define EXPORT_SYMBOL __declspec(dllexport)
+ #else
+ #define EXPORT_SYMBOL
+ #endif
+ #endif
+ #elif defined(HAVE_FUNC_ATTRIBUTE_VISIBILITY)
+ #define EXPORT_SYMBOL __attribute__ ((visibility ("default")))
+ #endif
+#elif defined(MSC_VER) && !defined(STATIC_LIBBITCOINCONSENSUS)
+ #define EXPORT_SYMBOL __declspec(dllimport)
+#endif
+
+#ifndef EXPORT_SYMBOL
+ #define EXPORT_SYMBOL
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BITCOINCONSENSUS_API_VER 0
+
+typedef enum bitcoinconsensus_error_t
+{
+ bitcoinconsensus_ERR_OK = 0,
+ bitcoinconsensus_ERR_TX_INDEX,
+ bitcoinconsensus_ERR_TX_SIZE_MISMATCH,
+ bitcoinconsensus_ERR_TX_DESERIALIZE,
+} bitcoinconsensus_error;
+
+/** Script verification flags */
+enum
+{
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0,
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
+};
+
+/// Returns 1 if the input nIn of the serialized transaction pointed to by
+/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
+/// the additional constraints specified by flags.
+/// If not NULL, err will contain an error/success code for the operation
+EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
+ const unsigned char *txTo , unsigned int txToLen,
+ unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
+
+EXPORT_SYMBOL unsigned int bitcoinconsensus_version();
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#undef EXPORT_SYMBOL
+
+#endif // BITCOIN_BITCOINCONSENSUS_H
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 54c2847f79..a10cefcc0b 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -13,18 +13,28 @@
#include "pubkey.h"
#include "script/script.h"
#include "uint256.h"
-#include "util.h"
using namespace std;
typedef vector<unsigned char> valtype;
-static const valtype vchFalse(0);
-static const valtype vchZero(0);
-static const valtype vchTrue(1, 1);
-static const CScriptNum bnZero(0);
-static const CScriptNum bnOne(1);
-static const CScriptNum bnFalse(0);
-static const CScriptNum bnTrue(1);
+
+namespace {
+
+inline bool set_success(ScriptError* ret)
+{
+ if (ret)
+ *ret = SCRIPT_ERR_OK;
+ return true;
+}
+
+inline bool set_error(ScriptError* ret, const ScriptError serror)
+{
+ if (ret)
+ *ret = serror;
+ return false;
+}
+
+} // anon namespace
bool CastToBool(const valtype& vch)
{
@@ -41,10 +51,10 @@ bool CastToBool(const valtype& vch)
return false;
}
-//
-// Script is a stack machine (like Forth) that evaluates a predicate
-// returning a bool indicating valid or not. There are no loops.
-//
+/**
+ * Script is a stack machine (like Forth) that evaluates a predicate
+ * returning a bool indicating valid or not. There are no loops.
+ */
#define stacktop(i) (stack.at(stack.size()+(i)))
#define altstacktop(i) (altstack.at(altstack.size()+(i)))
static inline void popstack(vector<valtype>& stack)
@@ -55,67 +65,105 @@ static inline void popstack(vector<valtype>& stack)
}
bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() < 33)
- return error("Non-canonical public key: too short");
+ if (vchPubKey.size() < 33) {
+ // Non-canonical public key: too short
+ return false;
+ }
if (vchPubKey[0] == 0x04) {
- if (vchPubKey.size() != 65)
- return error("Non-canonical public key: invalid length for uncompressed key");
+ if (vchPubKey.size() != 65) {
+ // Non-canonical public key: invalid length for uncompressed key
+ return false;
+ }
} else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
- if (vchPubKey.size() != 33)
- return error("Non-canonical public key: invalid length for compressed key");
+ if (vchPubKey.size() != 33) {
+ // Non-canonical public key: invalid length for compressed key
+ return false;
+ }
} else {
- return error("Non-canonical public key: neither compressed nor uncompressed");
+ // Non-canonical public key: neither compressed nor uncompressed
+ return false;
}
return true;
}
+/**
+ * A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
+ * Where R and S are not negative (their first byte has its highest bit not set), and not
+ * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
+ * in which case a single 0 byte is necessary and even required).
+ *
+ * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
+ */
bool static IsDERSignature(const valtype &vchSig) {
- // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
- // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
- // Where R and S are not negative (their first byte has its highest bit not set), and not
- // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
- // in which case a single 0 byte is necessary and even required).
- if (vchSig.size() < 9)
- return error("Non-canonical signature: too short");
- if (vchSig.size() > 73)
- return error("Non-canonical signature: too long");
- if (vchSig[0] != 0x30)
- return error("Non-canonical signature: wrong type");
- if (vchSig[1] != vchSig.size()-3)
- return error("Non-canonical signature: wrong length marker");
+
+ if (vchSig.size() < 9) {
+ // Non-canonical signature: too short
+ return false;
+ }
+ if (vchSig.size() > 73) {
+ // Non-canonical signature: too long
+ return false;
+ }
+ if (vchSig[0] != 0x30) {
+ // Non-canonical signature: wrong type
+ return false;
+ }
+ if (vchSig[1] != vchSig.size()-3) {
+ // Non-canonical signature: wrong length marker
+ return false;
+ }
unsigned int nLenR = vchSig[3];
- if (5 + nLenR >= vchSig.size())
- return error("Non-canonical signature: S length misplaced");
+ if (5 + nLenR >= vchSig.size()) {
+ // Non-canonical signature: S length misplaced
+ return false;
+ }
unsigned int nLenS = vchSig[5+nLenR];
- if ((unsigned long)(nLenR+nLenS+7) != vchSig.size())
- return error("Non-canonical signature: R+S length mismatch");
+ if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) {
+ // Non-canonical signature: R+S length mismatch
+ return false;
+ }
const unsigned char *R = &vchSig[4];
- if (R[-2] != 0x02)
- return error("Non-canonical signature: R value type mismatch");
- if (nLenR == 0)
- return error("Non-canonical signature: R length is zero");
- if (R[0] & 0x80)
- return error("Non-canonical signature: R value negative");
- if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80))
- return error("Non-canonical signature: R value excessively padded");
+ if (R[-2] != 0x02) {
+ // Non-canonical signature: R value type mismatch
+ return false;
+ }
+ if (nLenR == 0) {
+ // Non-canonical signature: R length is zero
+ return false;
+ }
+ if (R[0] & 0x80) {
+ // Non-canonical signature: R value negative
+ return false;
+ }
+ if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) {
+ // Non-canonical signature: R value excessively padded
+ return false;
+ }
const unsigned char *S = &vchSig[6+nLenR];
- if (S[-2] != 0x02)
- return error("Non-canonical signature: S value type mismatch");
- if (nLenS == 0)
- return error("Non-canonical signature: S length is zero");
- if (S[0] & 0x80)
- return error("Non-canonical signature: S value negative");
- if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
- return error("Non-canonical signature: S value excessively padded");
-
+ if (S[-2] != 0x02) {
+ // Non-canonical signature: S value type mismatch
+ return false;
+ }
+ if (nLenS == 0) {
+ // Non-canonical signature: S length is zero
+ return false;
+ }
+ if (S[0] & 0x80) {
+ // Non-canonical signature: S value negative
+ return false;
+ }
+ if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) {
+ // Non-canonical signature: S value excessively padded
+ return false;
+ }
return true;
}
-bool static IsLowDERSignature(const valtype &vchSig) {
+bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
if (!IsDERSignature(vchSig)) {
- return false;
+ return set_error(serror, SCRIPT_ERR_SIG_DER);
}
unsigned int nLenR = vchSig[3];
unsigned int nLenS = vchSig[5+nLenR];
@@ -124,7 +172,7 @@ bool static IsLowDERSignature(const valtype &vchSig) {
// complement modulo the order could have been used instead, which is
// one byte shorter when encoded correctly.
if (!eccrypto::CheckSignatureElement(S, nLenS, true))
- return error("Non-canonical signature: S value is unnecessarily high");
+ return set_error(serror, SCRIPT_ERR_SIG_HIGH_S);
return true;
}
@@ -135,25 +183,26 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
}
unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
- return error("Non-canonical signature: unknown hashtype byte");
+ return false;
return true;
}
-bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags) {
+bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(vchSig)) {
- return false;
- } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig)) {
+ return set_error(serror, SCRIPT_ERR_SIG_DER);
+ } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig, serror)) {
+ // serror is set
return false;
} else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSig)) {
- return false;
+ return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE);
}
return true;
}
-bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags) {
+bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) {
- return false;
+ return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
}
return true;
}
@@ -181,8 +230,16 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
return true;
}
-bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker)
+bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
+ static const CScriptNum bnZero(0);
+ static const CScriptNum bnOne(1);
+ static const CScriptNum bnFalse(0);
+ static const CScriptNum bnTrue(1);
+ static const valtype vchFalse(0);
+ static const valtype vchZero(0);
+ static const valtype vchTrue(1, 1);
+
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
@@ -190,8 +247,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
valtype vchPushValue;
vector<bool> vfExec;
vector<valtype> altstack;
+ set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if (script.size() > 10000)
- return false;
+ return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
int nOpCount = 0;
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
@@ -205,13 +263,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Read instruction
//
if (!script.GetOp(pc, opcode, vchPushValue))
- return false;
+ return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
- return false;
+ return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
// Note how OP_RESERVED does not count towards the opcode limit.
if (opcode > OP_16 && ++nOpCount > 201)
- return false;
+ return set_error(serror, SCRIPT_ERR_OP_COUNT);
if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
@@ -228,11 +286,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
opcode == OP_MOD ||
opcode == OP_LSHIFT ||
opcode == OP_RSHIFT)
- return false; // Disabled opcodes.
+ return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
- return false;
+ return set_error(serror, SCRIPT_ERR_MINIMALDATA);
}
stack.push_back(vchPushValue);
} else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
@@ -272,8 +330,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Control
//
case OP_NOP:
+ break;
+
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
+ {
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
+ }
break;
case OP_IF:
@@ -284,7 +348,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fExec)
{
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1);
fValue = CastToBool(vch);
if (opcode == OP_NOTIF)
@@ -298,7 +362,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_ELSE:
{
if (vfExec.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.back() = !vfExec.back();
}
break;
@@ -306,7 +370,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_ENDIF:
{
if (vfExec.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.pop_back();
}
break;
@@ -316,18 +380,18 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// (true -- ) or
// (false -- false) and return
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
bool fValue = CastToBool(stacktop(-1));
if (fValue)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_VERIFY);
}
break;
case OP_RETURN:
{
- return false;
+ return set_error(serror, SCRIPT_ERR_OP_RETURN);
}
break;
@@ -338,7 +402,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_TOALTSTACK:
{
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
altstack.push_back(stacktop(-1));
popstack(stack);
}
@@ -347,7 +411,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_FROMALTSTACK:
{
if (altstack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_ALTSTACK_OPERATION);
stack.push_back(altstacktop(-1));
popstack(altstack);
}
@@ -357,7 +421,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- )
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
popstack(stack);
}
@@ -367,7 +431,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- x1 x2 x1 x2)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-2);
valtype vch2 = stacktop(-1);
stack.push_back(vch1);
@@ -379,7 +443,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
if (stack.size() < 3)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-3);
valtype vch2 = stacktop(-2);
valtype vch3 = stacktop(-1);
@@ -393,7 +457,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
if (stack.size() < 4)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-4);
valtype vch2 = stacktop(-3);
stack.push_back(vch1);
@@ -405,7 +469,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
if (stack.size() < 6)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-6);
valtype vch2 = stacktop(-5);
stack.erase(stack.end()-6, stack.end()-4);
@@ -418,7 +482,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 x3 x4 -- x3 x4 x1 x2)
if (stack.size() < 4)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-4), stacktop(-2));
swap(stacktop(-3), stacktop(-1));
}
@@ -428,7 +492,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x - 0 | x x)
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1);
if (CastToBool(vch))
stack.push_back(vch);
@@ -447,7 +511,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x -- )
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
}
break;
@@ -456,7 +520,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x -- x x)
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1);
stack.push_back(vch);
}
@@ -466,7 +530,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- x2)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
stack.erase(stack.end() - 2);
}
break;
@@ -475,7 +539,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- x1 x2 x1)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-2);
stack.push_back(vch);
}
@@ -487,11 +551,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();
popstack(stack);
if (n < 0 || n >= (int)stack.size())
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-n-1);
if (opcode == OP_ROLL)
stack.erase(stack.end()-n-1);
@@ -505,7 +569,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// x2 x1 x3 after first swap
// x2 x3 x1 after second swap
if (stack.size() < 3)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-3), stacktop(-2));
swap(stacktop(-2), stacktop(-1));
}
@@ -515,7 +579,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- x2 x1)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-2), stacktop(-1));
}
break;
@@ -524,7 +588,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- x2 x1 x2)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1);
stack.insert(stack.end()-2, vch);
}
@@ -535,7 +599,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (in -- in size)
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn(stacktop(-1).size());
stack.push_back(bn.getvch());
}
@@ -551,7 +615,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 - bool)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-2);
valtype& vch2 = stacktop(-1);
bool fEqual = (vch1 == vch2);
@@ -568,7 +632,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fEqual)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_EQUALVERIFY);
}
}
break;
@@ -586,7 +650,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (in -- out)
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn(stacktop(-1), fRequireMinimal);
switch (opcode)
{
@@ -619,7 +683,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x1 x2 -- out)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn1(stacktop(-2), fRequireMinimal);
CScriptNum bn2(stacktop(-1), fRequireMinimal);
CScriptNum bn(0);
@@ -655,7 +719,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (CastToBool(stacktop(-1)))
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_NUMEQUALVERIFY);
}
}
break;
@@ -664,7 +728,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (x min max -- out)
if (stack.size() < 3)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn1(stacktop(-3), fRequireMinimal);
CScriptNum bn2(stacktop(-2), fRequireMinimal);
CScriptNum bn3(stacktop(-1), fRequireMinimal);
@@ -688,7 +752,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (in -- hash)
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch = stacktop(-1);
valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32);
if (opcode == OP_RIPEMD160)
@@ -718,7 +782,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{
// (sig pubkey -- bool)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vchSig = stacktop(-2);
valtype& vchPubKey = stacktop(-1);
@@ -729,11 +793,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));
- if (!CheckSignatureEncoding(vchSig, flags)) {
+ if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
+ //serror is set
return false;
}
-
- bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode);
+ bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode);
popstack(stack);
popstack(stack);
@@ -743,7 +807,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fSuccess)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
}
}
break;
@@ -755,26 +819,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
int i = 1;
if ((int)stack.size() < i)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
if (nKeysCount < 0 || nKeysCount > 20)
- return false;
+ return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT);
nOpCount += nKeysCount;
if (nOpCount > 201)
- return false;
+ return set_error(serror, SCRIPT_ERR_OP_COUNT);
int ikey = ++i;
i += nKeysCount;
if ((int)stack.size() < i)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int nSigsCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
if (nSigsCount < 0 || nSigsCount > nKeysCount)
- return false;
+ return set_error(serror, SCRIPT_ERR_SIG_COUNT);
int isig = ++i;
i += nSigsCount;
if ((int)stack.size() < i)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
@@ -792,12 +856,16 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
valtype& vchSig = stacktop(-isig);
valtype& vchPubKey = stacktop(-ikey);
- if (!CheckSignatureEncoding(vchSig, flags)) {
+ // Note how this makes the exact order of pubkey/signature evaluation
+ // distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
+ // See the script_(in)valid tests for details.
+ if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
+ // serror is set
return false;
}
// Check signature
- bool fOk = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode);
+ bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode);
if (fOk) {
isig++;
@@ -807,7 +875,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
nKeysCount--;
// If there are more signatures left than keys left,
- // then too many signatures have failed
+ // then too many signatures have failed. Exit early,
+ // without checking any further signatures.
if (nSigsCount > nKeysCount)
fSuccess = false;
}
@@ -823,9 +892,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// so optionally verify it is exactly equal to zero prior
// to removing it from the stack.
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size())
- return error("CHECKMULTISIG dummy argument not null");
+ return set_error(serror, SCRIPT_ERR_SIG_NULLDUMMY);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
@@ -835,44 +904,45 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fSuccess)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
}
break;
default:
- return false;
+ return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
// Size limits
if (stack.size() + altstack.size() > 1000)
- return false;
+ return set_error(serror, SCRIPT_ERR_STACK_SIZE);
}
}
catch (...)
{
- return false;
+ return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
}
if (!vfExec.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
- return true;
+ return set_success(serror);
}
namespace {
-/** Wrapper that serializes like CTransaction, but with the modifications
+/**
+ * Wrapper that serializes like CTransaction, but with the modifications
* required for the signature hash done in-place
*/
class CTransactionSignatureSerializer {
private:
- const CTransaction &txTo; // reference to the spending transaction (the one being serialized)
- const CScript &scriptCode; // output script being consumed
- const unsigned int nIn; // input index of txTo being signed
- const bool fAnyoneCanPay; // whether the hashtype has the SIGHASH_ANYONECANPAY flag set
- const bool fHashSingle; // whether the hashtype is SIGHASH_SINGLE
- const bool fHashNone; // whether the hashtype is SIGHASH_NONE
+ const CTransaction &txTo; //! reference to the spending transaction (the one being serialized)
+ const CScript &scriptCode; //! output script being consumed
+ const unsigned int nIn; //! input index of txTo being signed
+ const bool fAnyoneCanPay; //! whether the hashtype has the SIGHASH_ANYONECANPAY flag set
+ const bool fHashSingle; //! whether the hashtype is SIGHASH_SINGLE
+ const bool fHashNone; //! whether the hashtype is SIGHASH_NONE
public:
CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
@@ -951,7 +1021,7 @@ public:
::WriteCompactSize(s, nOutputs);
for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
SerializeOutput(s, nOutput, nType, nVersion);
- // Serialie nLockTime
+ // Serialize nLockTime
::Serialize(s, txTo.nLockTime, nType, nVersion);
}
};
@@ -961,14 +1031,14 @@ public:
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
if (nIn >= txTo.vin.size()) {
- LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
+ // nIn out of range
return 1;
}
// Check for invalid use of SIGHASH_SINGLE
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
if (nIn >= txTo.vout.size()) {
- LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nIn);
+ // nOut out of range
return 1;
}
}
@@ -1008,30 +1078,35 @@ bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vec
return true;
}
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker)
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
+ set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
+
if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) {
- return false;
+ return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
}
vector<vector<unsigned char> > stack, stackCopy;
- if (!EvalScript(stack, scriptSig, flags, checker))
+ if (!EvalScript(stack, scriptSig, flags, checker, serror))
+ // serror is set
return false;
if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack;
- if (!EvalScript(stack, scriptPubKey, flags, checker))
+ if (!EvalScript(stack, scriptPubKey, flags, checker, serror))
+ // serror is set
return false;
if (stack.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
if (CastToBool(stack.back()) == false)
- return false;
+ return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
// Additional validation for spend-to-script-hash transactions:
if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash())
{
- if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only
- return false; // or validation fails
+ // scriptSig must be literals-only or validation fails
+ if (!scriptSig.IsPushOnly())
+ return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
// stackCopy cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with
@@ -1042,12 +1117,16 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stackCopy);
- if (!EvalScript(stackCopy, pubKey2, flags, checker))
+ if (!EvalScript(stackCopy, pubKey2, flags, checker, serror))
+ // serror is set
return false;
if (stackCopy.empty())
- return false;
- return CastToBool(stackCopy.back());
+ return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
+ if (!CastToBool(stackCopy.back()))
+ return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
+ else
+ return set_success(serror);
}
- return true;
+ return set_success(serror);
}
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index ed899fc411..35b2f6c65a 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_SCRIPT_INTERPRETER_H
#define BITCOIN_SCRIPT_INTERPRETER_H
+#include "script_error.h"
+
#include <vector>
#include <stdint.h>
#include <string>
@@ -33,8 +35,8 @@ enum
SCRIPT_VERIFY_P2SH = (1U << 0),
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
- // Passing a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) to checksig causes that pubkey to be
- // skipped (not softfork safe: this flag can widen the validity of OP_CHECKSIG OP_NOT).
+ // Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
+ // (softfork safe, but not used or intended as a consensus rule).
SCRIPT_VERIFY_STRICTENC = (1U << 1),
// Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1)
@@ -55,7 +57,18 @@ enum
// any other push causes the script to fail (BIP62 rule 3).
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
// (softfork safe)
- SCRIPT_VERIFY_MINIMALDATA = (1U << 6)
+ SCRIPT_VERIFY_MINIMALDATA = (1U << 6),
+
+ // Discourage use of NOPs reserved for upgrades (NOP1-10)
+ //
+ // Provided so that nodes can avoid accepting or mining transactions
+ // containing executed NOP's whose meaning may change after a soft-fork,
+ // thus rendering the script invalid; with this flag set executing
+ // discouraged NOPs fails the script. This verification flag will never be
+ // a mandatory flag applied to scripts in a block. NOPs that are not
+ // executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7)
+
};
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
@@ -85,7 +98,7 @@ public:
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const;
};
-bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker);
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker);
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL);
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL);
#endif // BITCOIN_SCRIPT_INTERPRETER_H
diff --git a/src/script/script.h b/src/script/script.h
index e525ad13ee..9c22cb908c 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -179,12 +179,14 @@ public:
class CScriptNum
{
-// Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers.
-// The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
-// but results may overflow (and are valid as long as they are not used in a subsequent
-// numeric operation). CScriptNum enforces those semantics by storing results as
-// an int64 and allowing out-of-range values to be returned as a vector of bytes but
-// throwing an exception if arithmetic is done or the result is interpreted as an integer.
+/**
+ * Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers.
+ * The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
+ * but results may overflow (and are valid as long as they are not used in a subsequent
+ * numeric operation). CScriptNum enforces those semantics by storing results as
+ * an int64 and allowing out-of-range values to be returned as a vector of bytes but
+ * throwing an exception if arithmetic is done or the result is interpreted as an integer.
+ */
public:
explicit CScriptNum(const int64_t& n)
@@ -516,7 +518,7 @@ public:
return true;
}
- // Encode/decode small integers:
+ /** Encode/decode small integers: */
static int DecodeOP_N(opcodetype opcode)
{
if (opcode == OP_0)
@@ -560,25 +562,31 @@ public:
return nFound;
}
- // Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
- // as 20 sigops. With pay-to-script-hash, that changed:
- // CHECKMULTISIGs serialized in scriptSigs are
- // counted more accurately, assuming they are of the form
- // ... OP_N CHECKMULTISIG ...
+ /**
+ * Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
+ * as 20 sigops. With pay-to-script-hash, that changed:
+ * CHECKMULTISIGs serialized in scriptSigs are
+ * counted more accurately, assuming they are of the form
+ * ... OP_N CHECKMULTISIG ...
+ */
unsigned int GetSigOpCount(bool fAccurate) const;
- // Accurately count sigOps, including sigOps in
- // pay-to-script-hash transactions:
+ /**
+ * Accurately count sigOps, including sigOps in
+ * pay-to-script-hash transactions:
+ */
unsigned int GetSigOpCount(const CScript& scriptSig) const;
bool IsPayToScriptHash() const;
- // Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical).
+ /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
bool IsPushOnly() const;
- // Returns whether the script is guaranteed to fail at execution,
- // regardless of the initial stack. This allows outputs to be pruned
- // instantly when entering the UTXO set.
+ /**
+ * Returns whether the script is guaranteed to fail at execution,
+ * regardless of the initial stack. This allows outputs to be pruned
+ * instantly when entering the UTXO set.
+ */
bool IsUnspendable() const
{
return (size() > 0 && *begin() == OP_RETURN);
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
new file mode 100644
index 0000000000..5d24ed98ba
--- /dev/null
+++ b/src/script/script_error.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "script_error.h"
+
+const char* ScriptErrorString(const ScriptError serror)
+{
+ switch (serror)
+ {
+ case SCRIPT_ERR_OK:
+ return "No error";
+ case SCRIPT_ERR_EVAL_FALSE:
+ return "Script evaluated without error but finished with a false/empty top stack element";
+ case SCRIPT_ERR_VERIFY:
+ return "Script failed an OP_VERIFY operation";
+ case SCRIPT_ERR_EQUALVERIFY:
+ return "Script failed an OP_EQUALVERIFY operation";
+ case SCRIPT_ERR_CHECKMULTISIGVERIFY:
+ return "Script failed an OP_CHECKMULTISIGVERIFY operation";
+ case SCRIPT_ERR_CHECKSIGVERIFY:
+ return "Script failed an OP_CHECKSIGVERIFY operation";
+ case SCRIPT_ERR_NUMEQUALVERIFY:
+ return "Script failed an OP_NUMEQUALVERIFY operation";
+ case SCRIPT_ERR_SCRIPT_SIZE:
+ return "Script is too big";
+ case SCRIPT_ERR_PUSH_SIZE:
+ return "Push value size limit exceeded";
+ case SCRIPT_ERR_OP_COUNT:
+ return "Operation limit exceeded";
+ case SCRIPT_ERR_STACK_SIZE:
+ return "Stack size limit exceeded";
+ case SCRIPT_ERR_SIG_COUNT:
+ return "Signature count negative or greater than pubkey count";
+ case SCRIPT_ERR_PUBKEY_COUNT:
+ return "Pubkey count negative or limit exceeded";
+ case SCRIPT_ERR_BAD_OPCODE:
+ return "Opcode missing or not understood";
+ case SCRIPT_ERR_DISABLED_OPCODE:
+ return "Attempted to use a disabled opcode";
+ case SCRIPT_ERR_INVALID_STACK_OPERATION:
+ return "Operation not valid with the current stack size";
+ case SCRIPT_ERR_INVALID_ALTSTACK_OPERATION:
+ return "Operation not valid with the current altstack size";
+ case SCRIPT_ERR_OP_RETURN:
+ return "OP_RETURN was encountered";
+ case SCRIPT_ERR_UNBALANCED_CONDITIONAL:
+ return "Invalid OP_IF construction";
+ case SCRIPT_ERR_SIG_HASHTYPE:
+ return "Signature hash type missing or not understood";
+ case SCRIPT_ERR_SIG_DER:
+ return "Non-canonical DER signature";
+ case SCRIPT_ERR_MINIMALDATA:
+ return "Data push larger than necessary";
+ case SCRIPT_ERR_SIG_PUSHONLY:
+ return "Only non-push operators allowed in signatures";
+ case SCRIPT_ERR_SIG_HIGH_S:
+ return "Non-canonical signature: S value is unnecessarily high";
+ case SCRIPT_ERR_SIG_NULLDUMMY:
+ return "Dummy CHECKMULTISIG argument must be zero";
+ case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS:
+ return "NOPx reserved for soft-fork upgrades";
+ case SCRIPT_ERR_PUBKEYTYPE:
+ return "Public key is neither compressed or uncompressed";
+ case SCRIPT_ERR_UNKNOWN_ERROR:
+ case SCRIPT_ERR_ERROR_COUNT:
+ default: break;
+ }
+ return "unknown error";
+}
diff --git a/src/script/script_error.h b/src/script/script_error.h
new file mode 100644
index 0000000000..ac1f2deae5
--- /dev/null
+++ b/src/script/script_error.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_SCRIPT_ERROR_H
+#define BITCOIN_SCRIPT_ERROR_H
+
+typedef enum ScriptError_t
+{
+ SCRIPT_ERR_OK = 0,
+ SCRIPT_ERR_UNKNOWN_ERROR,
+ SCRIPT_ERR_EVAL_FALSE,
+ SCRIPT_ERR_OP_RETURN,
+
+ /* Max sizes */
+ SCRIPT_ERR_SCRIPT_SIZE,
+ SCRIPT_ERR_PUSH_SIZE,
+ SCRIPT_ERR_OP_COUNT,
+ SCRIPT_ERR_STACK_SIZE,
+ SCRIPT_ERR_SIG_COUNT,
+ SCRIPT_ERR_PUBKEY_COUNT,
+
+ /* Failed verify operations */
+ SCRIPT_ERR_VERIFY,
+ SCRIPT_ERR_EQUALVERIFY,
+ SCRIPT_ERR_CHECKMULTISIGVERIFY,
+ SCRIPT_ERR_CHECKSIGVERIFY,
+ SCRIPT_ERR_NUMEQUALVERIFY,
+
+ /* Logical/Format/Canonical errors */
+ SCRIPT_ERR_BAD_OPCODE,
+ SCRIPT_ERR_DISABLED_OPCODE,
+ SCRIPT_ERR_INVALID_STACK_OPERATION,
+ SCRIPT_ERR_INVALID_ALTSTACK_OPERATION,
+ SCRIPT_ERR_UNBALANCED_CONDITIONAL,
+
+ /* BIP62 */
+ SCRIPT_ERR_SIG_HASHTYPE,
+ SCRIPT_ERR_SIG_DER,
+ SCRIPT_ERR_MINIMALDATA,
+ SCRIPT_ERR_SIG_PUSHONLY,
+ SCRIPT_ERR_SIG_HIGH_S,
+ SCRIPT_ERR_SIG_NULLDUMMY,
+ SCRIPT_ERR_PUBKEYTYPE,
+
+ /* softfork safeness */
+ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
+
+ SCRIPT_ERR_ERROR_COUNT
+} ScriptError;
+
+#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
+
+const char* ScriptErrorString(const ScriptError error);
+
+#endif // BITCOIN_SCRIPT_ERROR_H
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index d76a5acd63..5580a5933e 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -15,13 +15,15 @@
namespace {
-// Valid signature cache, to avoid doing expensive ECDSA signature checking
-// twice for every transaction (once when accepted into memory pool, and
-// again when accepted into the block chain)
+/**
+ * Valid signature cache, to avoid doing expensive ECDSA signature checking
+ * twice for every transaction (once when accepted into memory pool, and
+ * again when accepted into the block chain)
+ */
class CSignatureCache
{
private:
- // sigdata_type is (signature hash, signature, public key):
+ //! sigdata_type is (signature hash, signature, public key):
typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type;
std::set< sigdata_type> setValid;
boost::shared_mutex cs_sigcache;
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 0eab0626e5..7dfed751b6 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -46,12 +46,12 @@ bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint2
return nSigned==nRequired;
}
-//
-// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type.
-// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
-// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
-// Returns false if scriptPubKey could not be completely satisfied.
-//
+/**
+ * Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type.
+ * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
+ * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
+ * Returns false if scriptPubKey could not be completely satisfied.
+ */
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType,
CScript& scriptSigRet, txnouttype& whichTypeRet)
{
@@ -144,9 +144,9 @@ static CScript PushAll(const vector<valtype>& values)
return result;
}
-static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& txTo, unsigned int nIn,
+static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
const vector<valtype>& vSolutions,
- vector<valtype>& sigs1, vector<valtype>& sigs2)
+ const vector<valtype>& sigs1, const vector<valtype>& sigs2)
{
// Combine all the signatures we've got:
set<valtype> allsigs;
@@ -199,7 +199,7 @@ static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction&
return result;
}
-static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn,
+static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
const txnouttype txType, const vector<valtype>& vSolutions,
vector<valtype>& sigs1, vector<valtype>& sigs2)
{
@@ -244,7 +244,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo,
return CScript();
}
-CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn,
+CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
const CScript& scriptSig1, const CScript& scriptSig2)
{
txnouttype txType;
diff --git a/src/script/sign.h b/src/script/sign.h
index c84d3f9a94..45a5e0dea3 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -17,8 +17,10 @@ struct CMutableTransaction;
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
-// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
-// combine them intelligently and return the result.
-CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2);
+/**
+ * Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
+ * combine them intelligently and return the result.
+ */
+CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2);
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index e238ecedb0..ab6e6cde0d 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -34,9 +34,9 @@ const char* GetTxnOutputType(txnouttype t)
return NULL;
}
-//
-// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
-//
+/**
+ * Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
+ */
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{
// Templates
diff --git a/src/script/standard.h b/src/script/standard.h
index 55a27881aa..c4b82b4c45 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -25,27 +25,32 @@ public:
CScriptID(const uint160& in) : uint160(in) {}
};
-static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes
+static const unsigned int MAX_OP_RETURN_RELAY = 40; //! bytes
extern unsigned nMaxDatacarrierBytes;
-// Mandatory script verification flags that all new blocks must comply with for
-// them to be valid. (but old blocks may not comply with) Currently just P2SH,
-// but in the future other flags may be added, such as a soft-fork to enforce
-// strict DER encoding.
-//
-// Failing one of these tests may trigger a DoS ban - see CheckInputs() for
-// details.
+/**
+ * Mandatory script verification flags that all new blocks must comply with for
+ * them to be valid. (but old blocks may not comply with) Currently just P2SH,
+ * but in the future other flags may be added, such as a soft-fork to enforce
+ * strict DER encoding.
+ *
+ * Failing one of these tests may trigger a DoS ban - see CheckInputs() for
+ * details.
+ */
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
-// Standard script verification flags that standard transactions will comply
-// with. However scripts violating these flags may still be present in valid
-// blocks and we must accept those blocks.
+/**
+ * Standard script verification flags that standard transactions will comply
+ * with. However scripts violating these flags may still be present in valid
+ * blocks and we must accept those blocks.
+ */
static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_STRICTENC |
SCRIPT_VERIFY_MINIMALDATA |
- SCRIPT_VERIFY_NULLDUMMY;
+ SCRIPT_VERIFY_NULLDUMMY |
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS;
-// For convenience, standard but not mandatory verify flags.
+/** For convenience, standard but not mandatory verify flags. */
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
enum txnouttype
@@ -65,7 +70,8 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
-/** A txout script template with a specific destination. It is either:
+/**
+ * A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination
* * CScriptID: TX_SCRIPTHASH destination