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!
tags/v0.15.1
Cory Fields 5 years ago
parent
commit
ab9edbd6b6
5 changed files with 306 additions and 119 deletions
  1. 2
    0
      src/Makefile.am
  2. 180
    117
      src/script/interpreter.cpp
  3. 4
    2
      src/script/interpreter.h
  4. 67
    0
      src/script/script_error.cpp
  5. 53
    0
      src/script/script_error.h

+ 2
- 0
src/Makefile.am View File

@@ -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.

+ 180
- 117
src/script/interpreter.cpp View File

@@ -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);
}

+ 4
- 2
src/script/interpreter.h View File

@@ -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
- 0
src/script/script_error.cpp View 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
- 0
src/script/script_error.h View 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…
Cancel
Save