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!
This commit is contained in:
parent
3d3ce7421e
commit
ab9edbd6b6
5 changed files with 306 additions and 119 deletions
|
@ -112,6 +112,7 @@ BITCOIN_CORE_H = \
|
|||
script/sigcache.h \
|
||||
script/sign.h \
|
||||
script/standard.h \
|
||||
script/script_error.h \
|
||||
serialize.h \
|
||||
streams.h \
|
||||
sync.h \
|
||||
|
@ -235,6 +236,7 @@ libbitcoin_common_a_SOURCES = \
|
|||
script/script.cpp \
|
||||
script/sign.cpp \
|
||||
script/standard.cpp \
|
||||
script/script_error.cpp \
|
||||
$(BITCOIN_CORE_H)
|
||||
|
||||
# util: shared between all executables.
|
||||
|
|
|
@ -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++)
|
||||
|
@ -55,16 +72,23 @@ 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;
|
||||
}
|
||||
|
@ -79,47 +103,74 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
|
|||
*/
|
||||
bool static IsDERSignature(const valtype &vchSig) {
|
||||
|
||||
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];
|
||||
|
@ -128,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;
|
||||
}
|
||||
|
@ -139,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;
|
||||
}
|
||||
|
@ -185,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();
|
||||
|
@ -194,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;
|
||||
|
||||
|
@ -209,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 ||
|
||||
|
@ -232,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))
|
||||
|
@ -288,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)
|
||||
|
@ -302,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;
|
||||
|
@ -310,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;
|
||||
|
@ -320,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;
|
||||
|
||||
|
@ -342,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);
|
||||
}
|
||||
|
@ -351,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);
|
||||
}
|
||||
|
@ -361,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);
|
||||
}
|
||||
|
@ -371,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);
|
||||
|
@ -383,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);
|
||||
|
@ -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)
|
||||
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);
|
||||
|
@ -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)
|
||||
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);
|
||||
|
@ -422,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));
|
||||
}
|
||||
|
@ -432,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);
|
||||
|
@ -451,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;
|
||||
|
@ -460,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);
|
||||
}
|
||||
|
@ -470,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;
|
||||
|
@ -479,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);
|
||||
}
|
||||
|
@ -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 - ... 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);
|
||||
|
@ -509,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));
|
||||
}
|
||||
|
@ -519,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;
|
||||
|
@ -528,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);
|
||||
}
|
||||
|
@ -539,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());
|
||||
}
|
||||
|
@ -555,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);
|
||||
|
@ -572,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;
|
||||
|
@ -590,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)
|
||||
{
|
||||
|
@ -623,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);
|
||||
|
@ -659,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;
|
||||
|
@ -668,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);
|
||||
|
@ -692,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)
|
||||
|
@ -722,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);
|
||||
|
@ -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
|
||||
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);
|
||||
|
@ -747,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;
|
||||
|
@ -759,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);
|
||||
|
@ -796,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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
// 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);
|
||||
|
@ -839,29 +893,29 @@ 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 {
|
||||
|
@ -966,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;
|
||||
}
|
||||
}
|
||||
|
@ -1013,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
|
||||
|
@ -1047,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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -85,7 +87,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
|
||||
|
|
67
src/script/script_error.cpp
Normal file
67
src/script/script_error.cpp
Normal file
|
@ -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
Normal file
53
src/script/script_error.h
Normal file
|
@ -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…
Reference in a new issue