Browse Source

script: create sane error return codes for script validation and remove logging

Attempt to codify the possible error statuses associated with script
validation. script/types.h has been created with the expectation that it will
be part of the public lib interface. The other flag enums will be moved here in
a future commit.

Logging has also been removed in order to drop the dependency on core.h. It can
be re-added to bitcoind as-needed. This makes script verification finally free
of application state and boost!
0.10
Cory Fields 10 years ago
parent
commit
ab9edbd6b6
  1. 2
      src/Makefile.am
  2. 297
      src/script/interpreter.cpp
  3. 6
      src/script/interpreter.h
  4. 67
      src/script/script_error.cpp
  5. 53
      src/script/script_error.h

2
src/Makefile.am

@ -112,6 +112,7 @@ BITCOIN_CORE_H = \
script/sigcache.h \ script/sigcache.h \
script/sign.h \ script/sign.h \
script/standard.h \ script/standard.h \
script/script_error.h \
serialize.h \ serialize.h \
streams.h \ streams.h \
sync.h \ sync.h \
@ -235,6 +236,7 @@ libbitcoin_common_a_SOURCES = \
script/script.cpp \ script/script.cpp \
script/sign.cpp \ script/sign.cpp \
script/standard.cpp \ script/standard.cpp \
script/script_error.cpp \
$(BITCOIN_CORE_H) $(BITCOIN_CORE_H)
# util: shared between all executables. # util: shared between all executables.

297
src/script/interpreter.cpp

@ -13,7 +13,6 @@
#include "pubkey.h" #include "pubkey.h"
#include "script/script.h" #include "script/script.h"
#include "uint256.h" #include "uint256.h"
#include "util.h"
using namespace std; using namespace std;
@ -26,6 +25,24 @@ static const CScriptNum bnOne(1);
static const CScriptNum bnFalse(0); static const CScriptNum bnFalse(0);
static const CScriptNum bnTrue(1); 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) bool CastToBool(const valtype& vch)
{ {
for (unsigned int i = 0; i < vch.size(); i++) for (unsigned int i = 0; i < vch.size(); i++)
@ -55,16 +72,23 @@ static inline void popstack(vector<valtype>& stack)
} }
bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
if (vchPubKey.size() < 33) if (vchPubKey.size() < 33) {
return error("Non-canonical public key: too short"); // Non-canonical public key: too short
return false;
}
if (vchPubKey[0] == 0x04) { if (vchPubKey[0] == 0x04) {
if (vchPubKey.size() != 65) if (vchPubKey.size() != 65) {
return error("Non-canonical public key: invalid length for uncompressed key"); // Non-canonical public key: invalid length for uncompressed key
return false;
}
} else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
if (vchPubKey.size() != 33) if (vchPubKey.size() != 33) {
return error("Non-canonical public key: invalid length for compressed key"); // Non-canonical public key: invalid length for compressed key
return false;
}
} else { } else {
return error("Non-canonical public key: neither compressed nor uncompressed"); // Non-canonical public key: neither compressed nor uncompressed
return false;
} }
return true; return true;
} }
@ -79,47 +103,74 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
*/ */
bool static IsDERSignature(const valtype &vchSig) { bool static IsDERSignature(const valtype &vchSig) {
if (vchSig.size() < 9) if (vchSig.size() < 9) {
return error("Non-canonical signature: too short"); // Non-canonical signature: too short
if (vchSig.size() > 73) return false;
return error("Non-canonical signature: too long"); }
if (vchSig[0] != 0x30) if (vchSig.size() > 73) {
return error("Non-canonical signature: wrong type"); // Non-canonical signature: too long
if (vchSig[1] != vchSig.size()-3) return false;
return error("Non-canonical signature: wrong length marker"); }
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]; unsigned int nLenR = vchSig[3];
if (5 + nLenR >= vchSig.size()) if (5 + nLenR >= vchSig.size()) {
return error("Non-canonical signature: S length misplaced"); // Non-canonical signature: S length misplaced
return false;
}
unsigned int nLenS = vchSig[5+nLenR]; unsigned int nLenS = vchSig[5+nLenR];
if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) {
return error("Non-canonical signature: R+S length mismatch"); // Non-canonical signature: R+S length mismatch
return false;
}
const unsigned char *R = &vchSig[4]; const unsigned char *R = &vchSig[4];
if (R[-2] != 0x02) if (R[-2] != 0x02) {
return error("Non-canonical signature: R value type mismatch"); // Non-canonical signature: R value type mismatch
if (nLenR == 0) return false;
return error("Non-canonical signature: R length is zero"); }
if (R[0] & 0x80) if (nLenR == 0) {
return error("Non-canonical signature: R value negative"); // Non-canonical signature: R length is zero
if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) return false;
return error("Non-canonical signature: R value excessively padded"); }
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]; const unsigned char *S = &vchSig[6+nLenR];
if (S[-2] != 0x02) if (S[-2] != 0x02) {
return error("Non-canonical signature: S value type mismatch"); // Non-canonical signature: S value type mismatch
if (nLenS == 0) return false;
return error("Non-canonical signature: S length is zero"); }
if (S[0] & 0x80) if (nLenS == 0) {
return error("Non-canonical signature: S value negative"); // Non-canonical signature: S length is zero
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) return false;
return error("Non-canonical signature: S value excessively padded"); }
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; return true;
} }
bool static IsLowDERSignature(const valtype &vchSig) { bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
if (!IsDERSignature(vchSig)) { if (!IsDERSignature(vchSig)) {
return false; return set_error(serror, SCRIPT_ERR_SIG_DER);
} }
unsigned int nLenR = vchSig[3]; unsigned int nLenR = vchSig[3];
unsigned int nLenS = vchSig[5+nLenR]; unsigned int nLenS = vchSig[5+nLenR];
@ -128,7 +179,7 @@ bool static IsLowDERSignature(const valtype &vchSig) {
// complement modulo the order could have been used instead, which is // complement modulo the order could have been used instead, which is
// one byte shorter when encoded correctly. // one byte shorter when encoded correctly.
if (!eccrypto::CheckSignatureElement(S, nLenS, true)) 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; return true;
} }
@ -139,18 +190,19 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
} }
unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
return error("Non-canonical signature: unknown hashtype byte"); return false;
return true; 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)) { if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(vchSig)) {
return false; return set_error(serror, SCRIPT_ERR_SIG_DER);
} else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig)) { } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig, serror)) {
// serror is set
return false; return false;
} else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSig)) { } else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSig)) {
return false; return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE);
} }
return true; return true;
} }
@ -185,7 +237,7 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
return true; 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 pc = script.begin();
CScript::const_iterator pend = script.end(); CScript::const_iterator pend = script.end();
@ -194,8 +246,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
valtype vchPushValue; valtype vchPushValue;
vector<bool> vfExec; vector<bool> vfExec;
vector<valtype> altstack; vector<valtype> altstack;
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if (script.size() > 10000) if (script.size() > 10000)
return false; return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
int nOpCount = 0; int nOpCount = 0;
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
@ -209,13 +262,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Read instruction // Read instruction
// //
if (!script.GetOp(pc, opcode, vchPushValue)) if (!script.GetOp(pc, opcode, vchPushValue))
return false; return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) 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. // Note how OP_RESERVED does not count towards the opcode limit.
if (opcode > OP_16 && ++nOpCount > 201) if (opcode > OP_16 && ++nOpCount > 201)
return false; return set_error(serror, SCRIPT_ERR_OP_COUNT);
if (opcode == OP_CAT || if (opcode == OP_CAT ||
opcode == OP_SUBSTR || opcode == OP_SUBSTR ||
@ -232,11 +285,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
opcode == OP_MOD || opcode == OP_MOD ||
opcode == OP_LSHIFT || opcode == OP_LSHIFT ||
opcode == OP_RSHIFT) 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 (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
return false; return set_error(serror, SCRIPT_ERR_MINIMALDATA);
} }
stack.push_back(vchPushValue); stack.push_back(vchPushValue);
} else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) } else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
@ -288,7 +341,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fExec) if (fExec)
{ {
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1); valtype& vch = stacktop(-1);
fValue = CastToBool(vch); fValue = CastToBool(vch);
if (opcode == OP_NOTIF) if (opcode == OP_NOTIF)
@ -302,7 +355,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_ELSE: case OP_ELSE:
{ {
if (vfExec.empty()) if (vfExec.empty())
return false; return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.back() = !vfExec.back(); vfExec.back() = !vfExec.back();
} }
break; break;
@ -310,7 +363,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_ENDIF: case OP_ENDIF:
{ {
if (vfExec.empty()) if (vfExec.empty())
return false; return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.pop_back(); vfExec.pop_back();
} }
break; break;
@ -320,18 +373,18 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// (true -- ) or // (true -- ) or
// (false -- false) and return // (false -- false) and return
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
bool fValue = CastToBool(stacktop(-1)); bool fValue = CastToBool(stacktop(-1));
if (fValue) if (fValue)
popstack(stack); popstack(stack);
else else
return false; return set_error(serror, SCRIPT_ERR_VERIFY);
} }
break; break;
case OP_RETURN: case OP_RETURN:
{ {
return false; return set_error(serror, SCRIPT_ERR_OP_RETURN);
} }
break; break;
@ -342,7 +395,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_TOALTSTACK: case OP_TOALTSTACK:
{ {
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
altstack.push_back(stacktop(-1)); altstack.push_back(stacktop(-1));
popstack(stack); popstack(stack);
} }
@ -351,7 +404,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
case OP_FROMALTSTACK: case OP_FROMALTSTACK:
{ {
if (altstack.size() < 1) if (altstack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_ALTSTACK_OPERATION);
stack.push_back(altstacktop(-1)); stack.push_back(altstacktop(-1));
popstack(altstack); popstack(altstack);
} }
@ -361,7 +414,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- ) // (x1 x2 -- )
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack); popstack(stack);
popstack(stack); popstack(stack);
} }
@ -371,7 +424,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- x1 x2 x1 x2) // (x1 x2 -- x1 x2 x1 x2)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-2); valtype vch1 = stacktop(-2);
valtype vch2 = stacktop(-1); valtype vch2 = stacktop(-1);
stack.push_back(vch1); stack.push_back(vch1);
@ -383,7 +436,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
if (stack.size() < 3) if (stack.size() < 3)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-3); valtype vch1 = stacktop(-3);
valtype vch2 = stacktop(-2); valtype vch2 = stacktop(-2);
valtype vch3 = stacktop(-1); valtype vch3 = stacktop(-1);
@ -397,7 +450,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
if (stack.size() < 4) if (stack.size() < 4)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-4); valtype vch1 = stacktop(-4);
valtype vch2 = stacktop(-3); valtype vch2 = stacktop(-3);
stack.push_back(vch1); stack.push_back(vch1);
@ -409,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) // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
if (stack.size() < 6) if (stack.size() < 6)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-6); valtype vch1 = stacktop(-6);
valtype vch2 = stacktop(-5); valtype vch2 = stacktop(-5);
stack.erase(stack.end()-6, stack.end()-4); stack.erase(stack.end()-6, stack.end()-4);
@ -422,7 +475,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 x3 x4 -- x3 x4 x1 x2) // (x1 x2 x3 x4 -- x3 x4 x1 x2)
if (stack.size() < 4) if (stack.size() < 4)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-4), stacktop(-2)); swap(stacktop(-4), stacktop(-2));
swap(stacktop(-3), stacktop(-1)); swap(stacktop(-3), stacktop(-1));
} }
@ -432,7 +485,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x - 0 | x x) // (x - 0 | x x)
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1); valtype vch = stacktop(-1);
if (CastToBool(vch)) if (CastToBool(vch))
stack.push_back(vch); stack.push_back(vch);
@ -451,7 +504,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x -- ) // (x -- )
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack); popstack(stack);
} }
break; break;
@ -460,7 +513,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x -- x x) // (x -- x x)
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1); valtype vch = stacktop(-1);
stack.push_back(vch); stack.push_back(vch);
} }
@ -470,7 +523,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- x2) // (x1 x2 -- x2)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
stack.erase(stack.end() - 2); stack.erase(stack.end() - 2);
} }
break; break;
@ -479,7 +532,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- x1 x2 x1) // (x1 x2 -- x1 x2 x1)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-2); valtype vch = stacktop(-2);
stack.push_back(vch); stack.push_back(vch);
} }
@ -491,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 - xn ... x2 x1 x0 xn)
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int n = CScriptNum(stacktop(-1), fRequireMinimal).getint(); int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();
popstack(stack); popstack(stack);
if (n < 0 || n >= (int)stack.size()) if (n < 0 || n >= (int)stack.size())
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-n-1); valtype vch = stacktop(-n-1);
if (opcode == OP_ROLL) if (opcode == OP_ROLL)
stack.erase(stack.end()-n-1); stack.erase(stack.end()-n-1);
@ -509,7 +562,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// x2 x1 x3 after first swap // x2 x1 x3 after first swap
// x2 x3 x1 after second swap // x2 x3 x1 after second swap
if (stack.size() < 3) if (stack.size() < 3)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-3), stacktop(-2)); swap(stacktop(-3), stacktop(-2));
swap(stacktop(-2), stacktop(-1)); swap(stacktop(-2), stacktop(-1));
} }
@ -519,7 +572,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- x2 x1) // (x1 x2 -- x2 x1)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-2), stacktop(-1)); swap(stacktop(-2), stacktop(-1));
} }
break; break;
@ -528,7 +581,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- x2 x1 x2) // (x1 x2 -- x2 x1 x2)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1); valtype vch = stacktop(-1);
stack.insert(stack.end()-2, vch); stack.insert(stack.end()-2, vch);
} }
@ -539,7 +592,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (in -- in size) // (in -- in size)
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn(stacktop(-1).size()); CScriptNum bn(stacktop(-1).size());
stack.push_back(bn.getvch()); stack.push_back(bn.getvch());
} }
@ -555,7 +608,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 - bool) // (x1 x2 - bool)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-2); valtype& vch1 = stacktop(-2);
valtype& vch2 = stacktop(-1); valtype& vch2 = stacktop(-1);
bool fEqual = (vch1 == vch2); bool fEqual = (vch1 == vch2);
@ -572,7 +625,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fEqual) if (fEqual)
popstack(stack); popstack(stack);
else else
return false; return set_error(serror, SCRIPT_ERR_EQUALVERIFY);
} }
} }
break; break;
@ -590,7 +643,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (in -- out) // (in -- out)
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn(stacktop(-1), fRequireMinimal); CScriptNum bn(stacktop(-1), fRequireMinimal);
switch (opcode) switch (opcode)
{ {
@ -623,7 +676,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x1 x2 -- out) // (x1 x2 -- out)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn1(stacktop(-2), fRequireMinimal); CScriptNum bn1(stacktop(-2), fRequireMinimal);
CScriptNum bn2(stacktop(-1), fRequireMinimal); CScriptNum bn2(stacktop(-1), fRequireMinimal);
CScriptNum bn(0); CScriptNum bn(0);
@ -659,7 +712,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (CastToBool(stacktop(-1))) if (CastToBool(stacktop(-1)))
popstack(stack); popstack(stack);
else else
return false; return set_error(serror, SCRIPT_ERR_NUMEQUALVERIFY);
} }
} }
break; break;
@ -668,7 +721,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (x min max -- out) // (x min max -- out)
if (stack.size() < 3) if (stack.size() < 3)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn1(stacktop(-3), fRequireMinimal); CScriptNum bn1(stacktop(-3), fRequireMinimal);
CScriptNum bn2(stacktop(-2), fRequireMinimal); CScriptNum bn2(stacktop(-2), fRequireMinimal);
CScriptNum bn3(stacktop(-1), fRequireMinimal); CScriptNum bn3(stacktop(-1), fRequireMinimal);
@ -692,7 +745,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (in -- hash) // (in -- hash)
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch = stacktop(-1); valtype& vch = stacktop(-1);
valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32);
if (opcode == OP_RIPEMD160) if (opcode == OP_RIPEMD160)
@ -722,7 +775,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
{ {
// (sig pubkey -- bool) // (sig pubkey -- bool)
if (stack.size() < 2) if (stack.size() < 2)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vchSig = stacktop(-2); valtype& vchSig = stacktop(-2);
valtype& vchPubKey = stacktop(-1); valtype& vchPubKey = stacktop(-1);
@ -733,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 // Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig)); scriptCode.FindAndDelete(CScript(vchSig));
if (!CheckSignatureEncoding(vchSig, flags)) { if (!CheckSignatureEncoding(vchSig, flags, serror)) {
//serror is set
return false; return false;
} }
bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode);
popstack(stack); popstack(stack);
@ -747,7 +800,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fSuccess) if (fSuccess)
popstack(stack); popstack(stack);
else else
return false; return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
} }
} }
break; break;
@ -759,26 +812,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
int i = 1; int i = 1;
if ((int)stack.size() < i) if ((int)stack.size() < i)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
if (nKeysCount < 0 || nKeysCount > 20) if (nKeysCount < 0 || nKeysCount > 20)
return false; return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT);
nOpCount += nKeysCount; nOpCount += nKeysCount;
if (nOpCount > 201) if (nOpCount > 201)
return false; return set_error(serror, SCRIPT_ERR_OP_COUNT);
int ikey = ++i; int ikey = ++i;
i += nKeysCount; i += nKeysCount;
if ((int)stack.size() < i) if ((int)stack.size() < i)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int nSigsCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); int nSigsCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
if (nSigsCount < 0 || nSigsCount > nKeysCount) if (nSigsCount < 0 || nSigsCount > nKeysCount)
return false; return set_error(serror, SCRIPT_ERR_SIG_COUNT);
int isig = ++i; int isig = ++i;
i += nSigsCount; i += nSigsCount;
if ((int)stack.size() < i) 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 // Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend); CScript scriptCode(pbegincodehash, pend);
@ -796,7 +849,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
valtype& vchSig = stacktop(-isig); valtype& vchSig = stacktop(-isig);
valtype& vchPubKey = stacktop(-ikey); valtype& vchPubKey = stacktop(-ikey);
if (!CheckSignatureEncoding(vchSig, flags)) { if (!CheckSignatureEncoding(vchSig, flags, serror)) {
// serror is set
return false; return false;
} }
@ -827,9 +881,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// so optionally verify it is exactly equal to zero prior // so optionally verify it is exactly equal to zero prior
// to removing it from the stack. // to removing it from the stack.
if (stack.size() < 1) if (stack.size() < 1)
return false; return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size()) 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); popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse); stack.push_back(fSuccess ? vchTrue : vchFalse);
@ -839,29 +893,29 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (fSuccess) if (fSuccess)
popstack(stack); popstack(stack);
else else
return false; return set_error(serror, SCRIPT_ERR_CHECKMULTISIGVERIFY);
} }
} }
break; break;
default: default:
return false; return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
} }
// Size limits // Size limits
if (stack.size() + altstack.size() > 1000) if (stack.size() + altstack.size() > 1000)
return false; return set_error(serror, SCRIPT_ERR_STACK_SIZE);
} }
} }
catch (...) catch (...)
{ {
return false; return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
} }
if (!vfExec.empty()) if (!vfExec.empty())
return false; return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
return true; return set_success(serror);
} }
namespace { namespace {
@ -966,14 +1020,14 @@ public:
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{ {
if (nIn >= txTo.vin.size()) { if (nIn >= txTo.vin.size()) {
LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); // nIn out of range
return 1; return 1;
} }
// Check for invalid use of SIGHASH_SINGLE // Check for invalid use of SIGHASH_SINGLE
if ((nHashType & 0x1f) == SIGHASH_SINGLE) { if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
if (nIn >= txTo.vout.size()) { if (nIn >= txTo.vout.size()) {
LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nIn); // nOut out of range
return 1; return 1;
} }
} }
@ -1013,30 +1067,35 @@ bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vec
return true; 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()) { if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) {
return false; return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
} }
vector<vector<unsigned char> > stack, stackCopy; vector<vector<unsigned char> > stack, stackCopy;
if (!EvalScript(stack, scriptSig, flags, checker)) if (!EvalScript(stack, scriptSig, flags, checker, serror))
// serror is set
return false; return false;
if (flags & SCRIPT_VERIFY_P2SH) if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack; stackCopy = stack;
if (!EvalScript(stack, scriptPubKey, flags, checker)) if (!EvalScript(stack, scriptPubKey, flags, checker, serror))
// serror is set
return false; return false;
if (stack.empty()) if (stack.empty())
return false; return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
if (CastToBool(stack.back()) == false) if (CastToBool(stack.back()) == false)
return false; return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
// Additional validation for spend-to-script-hash transactions: // Additional validation for spend-to-script-hash transactions:
if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash())
{ {
if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only // scriptSig must be literals-only or validation fails
return false; // or validation fails if (!scriptSig.IsPushOnly())
return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
// stackCopy cannot be empty here, because if it was the // stackCopy cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with // P2SH HASH <> EQUAL scriptPubKey would be evaluated with
@ -1047,12 +1106,16 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stackCopy); popstack(stackCopy);
if (!EvalScript(stackCopy, pubKey2, flags, checker)) if (!EvalScript(stackCopy, pubKey2, flags, checker, serror))
// serror is set
return false; return false;
if (stackCopy.empty()) if (stackCopy.empty())
return false; return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
return CastToBool(stackCopy.back()); if (!CastToBool(stackCopy.back()))
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
else
return set_success(serror);
} }
return true; return set_success(serror);
} }

6
src/script/interpreter.h

@ -6,6 +6,8 @@
#ifndef BITCOIN_SCRIPT_INTERPRETER_H #ifndef BITCOIN_SCRIPT_INTERPRETER_H
#define BITCOIN_SCRIPT_INTERPRETER_H #define BITCOIN_SCRIPT_INTERPRETER_H
#include "script_error.h"
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
@ -85,7 +87,7 @@ public:
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const; 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 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); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL);
#endif // BITCOIN_SCRIPT_INTERPRETER_H #endif // BITCOIN_SCRIPT_INTERPRETER_H

67
src/script/script_error.cpp

@ -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.
#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_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;
}
return "unknown error";
}

53
src/script/script_error.h

@ -0,0 +1,53 @@
// 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_ERROR_COUNT
} ScriptError;
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
const char* ScriptErrorString(const ScriptError error);
#endif // BITCOIN_SCRIPT_ERROR_H
Loading…
Cancel
Save