diff options
Diffstat (limited to 'src/script/interpreter.cpp')
-rw-r--r-- | src/script/interpreter.cpp | 336 |
1 files changed, 202 insertions, 134 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 54c2847f79..cf81fe30a2 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -13,7 +13,6 @@ #include "pubkey.h" #include "script/script.h" #include "uint256.h" -#include "util.h" using namespace std; @@ -26,6 +25,24 @@ 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) { for (unsigned int i = 0; i < vch.size(); i++) @@ -41,10 +58,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 +72,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 +179,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,18 +190,19 @@ 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; } @@ -181,7 +237,7 @@ 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) { CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); @@ -190,8 +246,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 +262,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 +285,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)) @@ -284,7 +341,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 +355,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 +363,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 +373,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 +395,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 +404,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 +414,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 +424,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 +436,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 +450,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 +462,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 +475,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 +485,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 +504,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 +513,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 +523,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 +532,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 +544,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 +562,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 +572,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 +581,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 +592,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 +608,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 +625,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 +643,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 +676,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 +712,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 +721,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 +745,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 +775,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,10 +786,10 @@ 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)) { + //serror is set return false; } - bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); popstack(stack); @@ -743,7 +800,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 +812,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,7 +849,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un valtype& vchSig = stacktop(-isig); valtype& vchPubKey = stacktop(-ikey); - if (!CheckSignatureEncoding(vchSig, flags)) { + if (!CheckSignatureEncoding(vchSig, flags, serror)) { + // serror is set return false; } @@ -823,9 +881,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 +893,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 +1010,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 +1020,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 +1067,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 +1106,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); } |