diff options
Diffstat (limited to 'src/script')
-rw-r--r-- | src/script/bitcoinconsensus.cpp | 91 | ||||
-rw-r--r-- | src/script/bitcoinconsensus.h | 67 | ||||
-rw-r--r-- | src/script/interpreter.cpp | 371 | ||||
-rw-r--r-- | src/script/interpreter.h | 23 | ||||
-rw-r--r-- | src/script/script.h | 44 | ||||
-rw-r--r-- | src/script/script_error.cpp | 71 | ||||
-rw-r--r-- | src/script/script_error.h | 57 | ||||
-rw-r--r-- | src/script/sigcache.cpp | 10 | ||||
-rw-r--r-- | src/script/sign.cpp | 12 | ||||
-rw-r--r-- | src/script/sign.h | 6 | ||||
-rw-r--r-- | src/script/standard.cpp | 6 | ||||
-rw-r--r-- | src/script/standard.h | 34 |
12 files changed, 594 insertions, 198 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 9dfd640dfb..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) { diff --git a/src/script/sign.h b/src/script/sign.h index 99d5516adb..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. +/** + * 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 |