aboutsummaryrefslogtreecommitdiff
path: root/src/script/interpreter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/interpreter.cpp')
-rw-r--r--src/script/interpreter.cpp583
1 files changed, 344 insertions, 239 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index a71f55dd2..8a06f3d11 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1,32 +1,40 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin developers
+// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "interpreter.h"
-#include "core.h"
+#include "primitives/transaction.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
-#include "crypto/sha2.h"
-#include "random.h"
+#include "crypto/sha256.h"
+#include "eccryptoverify.h"
+#include "pubkey.h"
#include "script/script.h"
#include "uint256.h"
-#include "util.h"
-
-#include <boost/thread.hpp>
-#include <boost/tuple/tuple_comparison.hpp>
using namespace std;
typedef vector<unsigned char> valtype;
-static const valtype vchFalse(0);
-static const valtype vchZero(0);
-static const valtype vchTrue(1, 1);
-static const CScriptNum bnZero(0);
-static const CScriptNum bnOne(1);
-static const CScriptNum bnFalse(0);
-static const CScriptNum bnTrue(1);
+
+namespace {
+
+inline bool set_success(ScriptError* ret)
+{
+ if (ret)
+ *ret = SCRIPT_ERR_OK;
+ return true;
+}
+
+inline bool set_error(ScriptError* ret, const ScriptError serror)
+{
+ if (ret)
+ *ret = serror;
+ return false;
+}
+
+} // anon namespace
bool CastToBool(const valtype& vch)
{
@@ -43,10 +51,10 @@ bool CastToBool(const valtype& vch)
return false;
}
-//
-// Script is a stack machine (like Forth) that evaluates a predicate
-// returning a bool indicating valid or not. There are no loops.
-//
+/**
+ * Script is a stack machine (like Forth) that evaluates a predicate
+ * returning a bool indicating valid or not. There are no loops.
+ */
#define stacktop(i) (stack.at(stack.size()+(i)))
#define altstacktop(i) (altstack.at(altstack.size()+(i)))
static inline void popstack(vector<valtype>& stack)
@@ -56,84 +64,187 @@ static inline void popstack(vector<valtype>& stack)
stack.pop_back();
}
-bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) {
- if (!(flags & SCRIPT_VERIFY_STRICTENC))
- return true;
-
- if (vchPubKey.size() < 33)
- return error("Non-canonical public key: too short");
+bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
+ 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;
}
-bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) {
- if (!(flags & SCRIPT_VERIFY_STRICTENC))
- return true;
+/**
+ * A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
+ * Where R and S are not negative (their first byte has its highest bit not set), and not
+ * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
+ * in which case a single 0 byte is necessary and even required).
+ *
+ * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
+ */
+bool static IsDERSignature(const valtype &vchSig) {
- // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
- // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
- // Where R and S are not negative (their first byte has its highest bit not set), and not
- // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
- // in which case a single 0 byte is necessary and even required).
- if (vchSig.size() < 9)
- return error("Non-canonical signature: too short");
- if (vchSig.size() > 73)
- return error("Non-canonical signature: too long");
- unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
- if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
- return error("Non-canonical signature: unknown hashtype byte");
- 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) {
+ // 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, ScriptError* serror) {
+ if (!IsDERSignature(vchSig)) {
+ return set_error(serror, SCRIPT_ERR_SIG_DER);
+ }
+ unsigned int nLenR = vchSig[3];
+ unsigned int nLenS = vchSig[5+nLenR];
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 (flags & SCRIPT_VERIFY_LOW_S) {
- // If the S value is above the order of the curve divided by two, its
- // complement modulo the order could have been used instead, which is
- // one byte shorter when encoded correctly.
- if (!CKey::CheckSignatureElement(S, nLenS, true))
- return error("Non-canonical signature: S value is unnecessarily high");
+ // If the S value is above the order of the curve divided by two, its
+ // complement modulo the order could have been used instead, which is
+ // one byte shorter when encoded correctly.
+ if (!eccrypto::CheckSignatureElement(S, nLenS, true))
+ return set_error(serror, SCRIPT_ERR_SIG_HIGH_S);
+
+ return true;
+}
+
+bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
+ if (vchSig.size() == 0) {
+ return false;
+ }
+ unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
+ if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
+ return false;
+
+ return true;
+}
+
+bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
+ // Empty signature. Not strictly DER encoded, but allowed to provide a
+ // compact way to provide an invalid signature for use with CHECK(MULTI)SIG
+ if (vchSig.size() == 0) {
+ return true;
+ }
+ if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(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 set_error(serror, SCRIPT_ERR_SIG_HASHTYPE);
+ }
+ return true;
+}
+
+bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
+ if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) {
+ return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
}
+ return true;
+}
+bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
+ if (data.size() == 0) {
+ // Could have used OP_0.
+ return opcode == OP_0;
+ } else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) {
+ // Could have used OP_1 .. OP_16.
+ return opcode == OP_1 + (data[0] - 1);
+ } else if (data.size() == 1 && data[0] == 0x81) {
+ // Could have used OP_1NEGATE.
+ return opcode == OP_1NEGATE;
+ } else if (data.size() <= 75) {
+ // Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
+ return opcode == data.size();
+ } else if (data.size() <= 255) {
+ // Could have used OP_PUSHDATA.
+ return opcode == OP_PUSHDATA1;
+ } else if (data.size() <= 65535) {
+ // Could have used OP_PUSHDATA2.
+ return opcode == OP_PUSHDATA2;
+ }
return true;
}
-bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags)
+bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
+ static const CScriptNum bnZero(0);
+ static const CScriptNum bnOne(1);
+ static const CScriptNum bnFalse(0);
+ static const CScriptNum bnTrue(1);
+ static const valtype vchFalse(0);
+ static const valtype vchZero(0);
+ static const valtype vchTrue(1, 1);
+
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
@@ -141,9 +252,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
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;
try
{
@@ -155,13 +268,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// 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 ||
@@ -178,11 +291,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
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 (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
+ if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
+ return set_error(serror, SCRIPT_ERR_MINIMALDATA);
+ }
stack.push_back(vchPushValue);
- else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
+ } else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
switch (opcode)
{
//
@@ -209,6 +325,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// ( -- value)
CScriptNum bn((int)opcode - (int)(OP_1 - 1));
stack.push_back(bn.getvch());
+ // The result of these opcodes should always be the minimal way to push the data
+ // they push, so no need for a CheckMinimalPush here.
}
break;
@@ -217,8 +335,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Control
//
case OP_NOP:
+ break;
+
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
+ {
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
+ }
break;
case OP_IF:
@@ -229,7 +353,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
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)
@@ -243,7 +367,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
case OP_ELSE:
{
if (vfExec.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.back() = !vfExec.back();
}
break;
@@ -251,7 +375,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
case OP_ENDIF:
{
if (vfExec.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.pop_back();
}
break;
@@ -261,18 +385,18 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (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;
@@ -283,7 +407,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
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);
}
@@ -292,7 +416,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
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);
}
@@ -302,7 +426,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (x1 x2 -- )
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
popstack(stack);
}
@@ -312,7 +436,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -324,7 +448,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -338,7 +462,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -350,7 +474,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -363,7 +487,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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));
}
@@ -373,7 +497,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -392,7 +516,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (x -- )
if (stack.size() < 1)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
}
break;
@@ -401,7 +525,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
}
@@ -411,7 +535,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (x1 x2 -- x2)
if (stack.size() < 2)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
stack.erase(stack.end() - 2);
}
break;
@@ -420,7 +544,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
}
@@ -432,11 +556,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (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;
- int n = CScriptNum(stacktop(-1)).getint();
+ 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);
@@ -450,7 +574,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// 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));
}
@@ -460,7 +584,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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;
@@ -469,7 +593,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
}
@@ -480,7 +604,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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());
}
@@ -496,7 +620,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -513,7 +637,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if (fEqual)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_EQUALVERIFY);
}
}
break;
@@ -531,8 +655,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (in -- out)
if (stack.size() < 1)
- return false;
- CScriptNum bn(stacktop(-1));
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+ CScriptNum bn(stacktop(-1), fRequireMinimal);
switch (opcode)
{
case OP_1ADD: bn += bnOne; break;
@@ -564,9 +688,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (x1 x2 -- out)
if (stack.size() < 2)
- return false;
- CScriptNum bn1(stacktop(-2));
- CScriptNum bn2(stacktop(-1));
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+ CScriptNum bn1(stacktop(-2), fRequireMinimal);
+ CScriptNum bn2(stacktop(-1), fRequireMinimal);
CScriptNum bn(0);
switch (opcode)
{
@@ -600,7 +724,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if (CastToBool(stacktop(-1)))
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_NUMEQUALVERIFY);
}
}
break;
@@ -609,10 +733,10 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (x min max -- out)
if (stack.size() < 3)
- return false;
- CScriptNum bn1(stacktop(-3));
- CScriptNum bn2(stacktop(-2));
- CScriptNum bn3(stacktop(-1));
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+ CScriptNum bn1(stacktop(-3), fRequireMinimal);
+ CScriptNum bn2(stacktop(-2), fRequireMinimal);
+ CScriptNum bn3(stacktop(-1), fRequireMinimal);
bool fValue = (bn2 <= bn1 && bn1 < bn3);
popstack(stack);
popstack(stack);
@@ -633,7 +757,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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)
@@ -663,7 +787,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
{
// (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);
@@ -674,8 +798,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));
- bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) &&
- CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, flags);
+ if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
+ //serror is set
+ return false;
+ }
+ bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode);
popstack(stack);
popstack(stack);
@@ -685,7 +812,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if (fSuccess)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
}
}
break;
@@ -697,26 +824,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
int i = 1;
if ((int)stack.size() < i)
- return false;
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
- int nKeysCount = CScriptNum(stacktop(-i)).getint();
+ 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)).getint();
+ 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);
@@ -734,9 +861,16 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
valtype& vchSig = stacktop(-isig);
valtype& vchPubKey = stacktop(-ikey);
+ // Note how this makes the exact order of pubkey/signature evaluation
+ // distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
+ // See the script_(in)valid tests for details.
+ if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
+ // serror is set
+ return false;
+ }
+
// Check signature
- bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) &&
- CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, flags);
+ bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode);
if (fOk) {
isig++;
@@ -746,7 +880,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
nKeysCount--;
// If there are more signatures left than keys left,
- // then too many signatures have failed
+ // then too many signatures have failed. Exit early,
+ // without checking any further signatures.
if (nSigsCount > nKeysCount)
fSuccess = false;
}
@@ -762,9 +897,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// 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);
@@ -774,44 +909,45 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if (fSuccess)
popstack(stack);
else
- return false;
+ return set_error(serror, SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
}
break;
default:
- return false;
+ return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
// Size limits
if (stack.size() + altstack.size() > 1000)
- return false;
+ return set_error(serror, SCRIPT_ERR_STACK_SIZE);
}
}
catch (...)
{
- return false;
+ return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
}
if (!vfExec.empty())
- return false;
+ return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
- return true;
+ return set_success(serror);
}
namespace {
-/** Wrapper that serializes like CTransaction, but with the modifications
+/**
+ * Wrapper that serializes like CTransaction, but with the modifications
* required for the signature hash done in-place
*/
class CTransactionSignatureSerializer {
private:
- const CTransaction &txTo; // reference to the spending transaction (the one being serialized)
- const CScript &scriptCode; // output script being consumed
- const unsigned int nIn; // input index of txTo being signed
- const bool fAnyoneCanPay; // whether the hashtype has the SIGHASH_ANYONECANPAY flag set
- const bool fHashSingle; // whether the hashtype is SIGHASH_SINGLE
- const bool fHashNone; // whether the hashtype is SIGHASH_NONE
+ const CTransaction &txTo; //! reference to the spending transaction (the one being serialized)
+ const CScript &scriptCode; //! output script being consumed
+ const unsigned int nIn; //! input index of txTo being signed
+ const bool fAnyoneCanPay; //! whether the hashtype has the SIGHASH_ANYONECANPAY flag set
+ const bool fHashSingle; //! whether the hashtype is SIGHASH_SINGLE
+ const bool fHashNone; //! whether the hashtype is SIGHASH_NONE
public:
CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
@@ -890,25 +1026,26 @@ public:
::WriteCompactSize(s, nOutputs);
for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
SerializeOutput(s, nOutput, nType, nVersion);
- // Serialie nLockTime
+ // Serialize nLockTime
::Serialize(s, txTo.nLockTime, nType, nVersion);
}
};
} // anon namespace
-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)
{
+ static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
if (nIn >= txTo.vin.size()) {
- LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
- return 1;
+ // nIn out of range
+ return one;
}
// 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);
- return 1;
+ // nOut out of range
+ return one;
}
}
@@ -921,70 +1058,19 @@ uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsig
return ss.GetHash();
}
-// Valid signature cache, to avoid doing expensive ECDSA signature checking
-// twice for every transaction (once when accepted into memory pool, and
-// again when accepted into the block chain)
-class CSignatureCache
+bool SignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
-private:
- // sigdata_type is (signature hash, signature, public key):
- typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type;
- std::set< sigdata_type> setValid;
- boost::shared_mutex cs_sigcache;
-
-public:
- bool
- Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
- {
- boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
-
- sigdata_type k(hash, vchSig, pubKey);
- std::set<sigdata_type>::iterator mi = setValid.find(k);
- if (mi != setValid.end())
- return true;
- return false;
- }
-
- void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
- {
- // DoS prevention: limit cache size to less than 10MB
- // (~200 bytes per cache entry times 50,000 entries)
- // Since there are a maximum of 20,000 signature operations per block
- // 50,000 is a reasonable default.
- int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000);
- if (nMaxCacheSize <= 0) return;
-
- boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
-
- while (static_cast<int64_t>(setValid.size()) > nMaxCacheSize)
- {
- // Evict a random entry. Random because that helps
- // foil would-be DoS attackers who might try to pre-generate
- // and re-use a set of valid signatures just-slightly-greater
- // than our cache size.
- uint256 randomHash = GetRandHash();
- std::vector<unsigned char> unused;
- std::set<sigdata_type>::iterator it =
- setValid.lower_bound(sigdata_type(randomHash, unused, unused));
- if (it == setValid.end())
- it = setValid.begin();
- setValid.erase(*it);
- }
-
- sigdata_type k(hash, vchSig, pubKey);
- setValid.insert(k);
- }
-};
+ return pubkey.Verify(sighash, vchSig);
+}
-bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char>& vchPubKey, const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int flags)
+bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vector<unsigned char>& vchPubKey, const CScript& scriptCode) const
{
- static CSignatureCache signatureCache;
-
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
return false;
// Hash type is one byte tacked on to the end of the signature
+ vector<unsigned char> vchSig(vchSigIn);
if (vchSig.empty())
return false;
int nHashType = vchSig.back();
@@ -992,54 +1078,73 @@ bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char>& vchPubK
uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType);
- if (signatureCache.Get(sighash, vchSig, pubkey))
- return true;
-
- if (!pubkey.Verify(sighash, vchSig))
+ if (!VerifySignature(vchSig, pubkey, sighash))
return false;
- if (!(flags & SCRIPT_VERIFY_NOCACHE))
- signatureCache.Set(sighash, vchSig, pubkey);
-
return true;
}
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags)
+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 set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
+ }
+
vector<vector<unsigned char> > stack, stackCopy;
- if (!EvalScript(stack, scriptSig, txTo, nIn, flags))
+ if (!EvalScript(stack, scriptSig, flags, checker, serror))
+ // serror is set
return false;
if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack;
- if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags))
+ 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);
+
+ // Restore stack.
+ swap(stack, stackCopy);
- // stackCopy cannot be empty here, because if it was the
+ // stack cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with
// an empty stack and the EvalScript above would return false.
- assert(!stackCopy.empty());
+ assert(!stack.empty());
- const valtype& pubKeySerialized = stackCopy.back();
+ const valtype& pubKeySerialized = stack.back();
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
- popstack(stackCopy);
+ popstack(stack);
- if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags))
+ if (!EvalScript(stack, pubKey2, flags, checker, serror))
+ // serror is set
return false;
- if (stackCopy.empty())
- return false;
- return CastToBool(stackCopy.back());
+ if (stack.empty())
+ return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
+ if (!CastToBool(stack.back()))
+ return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
}
- return true;
+ // The CLEANSTACK check is only performed after potential P2SH evaluation,
+ // as the non-P2SH evaluation of a P2SH script will obviously not result in
+ // a clean stack (the P2SH inputs remain).
+ if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) {
+ // Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK
+ // would be possible, which is not a softfork (and P2SH should be one).
+ assert((flags & SCRIPT_VERIFY_P2SH) != 0);
+ if (stack.size() != 1) {
+ return set_error(serror, SCRIPT_ERR_CLEANSTACK);
+ }
+ }
+
+ return set_success(serror);
}