Changeset View
Changeset View
Standalone View
Standalone View
src/script/interpreter.cpp
Show First 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | switch (opcode) { | ||||
break; | break; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool EvalScript(std::vector<valtype> &stack, const CScript &script, | bool EvalScript(std::vector<valtype> &stack, const CScript &script, | ||||
uint32_t flags, const BaseSignatureChecker &checker, | uint32_t flags, const BaseSignatureChecker &checker, | ||||
ScriptError *serror) { | ScriptError *serror, int *pnSigChecks) { | ||||
deadalnix: Pass a reference to some metric counter object instead of an `int*`. If you are worried about… | |||||
static const CScriptNum bnZero(0); | static const CScriptNum bnZero(0); | ||||
static const CScriptNum bnOne(1); | static const CScriptNum bnOne(1); | ||||
static const valtype vchFalse(0); | static const valtype vchFalse(0); | ||||
static const valtype vchTrue(1, 1); | static const valtype vchTrue(1, 1); | ||||
CScript::const_iterator pc = script.begin(); | CScript::const_iterator pc = script.begin(); | ||||
CScript::const_iterator pend = script.end(); | CScript::const_iterator pend = script.end(); | ||||
CScript::const_iterator pbegincodehash = script.begin(); | CScript::const_iterator pbegincodehash = script.begin(); | ||||
▲ Show 20 Lines • Show All 784 Lines • ▼ Show 20 Lines | try { | ||||
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, | bool fSuccess = checker.CheckSig(vchSig, vchPubKey, | ||||
scriptCode, flags); | scriptCode, flags); | ||||
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && | if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && | ||||
vchSig.size()) { | vchSig.size()) { | ||||
return set_error(serror, ScriptError::SIG_NULLFAIL); | return set_error(serror, ScriptError::SIG_NULLFAIL); | ||||
} | } | ||||
if (fSuccess && pnSigChecks) { | |||||
*pnSigChecks += 1; | |||||
deadalnixUnsubmitted Not Done Inline ActionsmetricCounter.addCheckSig(1); deadalnix: metricCounter.addCheckSig(1);
| |||||
} | |||||
popstack(stack); | popstack(stack); | ||||
popstack(stack); | popstack(stack); | ||||
stack.push_back(fSuccess ? vchTrue : vchFalse); | stack.push_back(fSuccess ? vchTrue : vchFalse); | ||||
if (opcode == OP_CHECKSIGVERIFY) { | if (opcode == OP_CHECKSIGVERIFY) { | ||||
if (fSuccess) { | if (fSuccess) { | ||||
popstack(stack); | popstack(stack); | ||||
} else { | } else { | ||||
return set_error(serror, | return set_error(serror, | ||||
Show All 31 Lines | try { | ||||
vchSig, CPubKey(vchPubKey), uint256(vchHash)); | vchSig, CPubKey(vchPubKey), uint256(vchHash)); | ||||
} | } | ||||
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && | if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && | ||||
vchSig.size()) { | vchSig.size()) { | ||||
return set_error(serror, ScriptError::SIG_NULLFAIL); | return set_error(serror, ScriptError::SIG_NULLFAIL); | ||||
} | } | ||||
if (fSuccess && pnSigChecks) { | |||||
*pnSigChecks += 1; | |||||
} | |||||
popstack(stack); | popstack(stack); | ||||
popstack(stack); | popstack(stack); | ||||
popstack(stack); | popstack(stack); | ||||
stack.push_back(fSuccess ? vchTrue : vchFalse); | stack.push_back(fSuccess ? vchTrue : vchFalse); | ||||
if (opcode == OP_CHECKDATASIGVERIFY) { | if (opcode == OP_CHECKDATASIGVERIFY) { | ||||
if (fSuccess) { | if (fSuccess) { | ||||
popstack(stack); | popstack(stack); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | try { | ||||
} | } | ||||
if ((checkBits >> iKey) != 0) { | if ((checkBits >> iKey) != 0) { | ||||
// This is a sanity check and should be | // This is a sanity check and should be | ||||
// unrecheable. | // unrecheable. | ||||
return set_error( | return set_error( | ||||
serror, ScriptError::INVALID_BIT_COUNT); | serror, ScriptError::INVALID_BIT_COUNT); | ||||
} | } | ||||
// fSuccess is guaranteed true here, but to keep | |||||
// form: | |||||
if (fSuccess && pnSigChecks) { | |||||
*pnSigChecks += nSigsCount; | |||||
} | |||||
} else { | } else { | ||||
// LEGACY MULTISIG (ECDSA / NULL) | // LEGACY MULTISIG (ECDSA / NULL) | ||||
// Remove signature for pre-fork scripts | // Remove signature for pre-fork scripts | ||||
for (int k = 0; k < nSigsCount; k++) { | for (int k = 0; k < nSigsCount; k++) { | ||||
valtype &vchSig = stacktop(-idxTopSig - k); | valtype &vchSig = stacktop(-idxTopSig - k); | ||||
CleanupScriptCode(scriptCode, vchSig, flags); | CleanupScriptCode(scriptCode, vchSig, flags); | ||||
} | } | ||||
Show All 31 Lines | |||||
// If there are more signatures left than keys | // If there are more signatures left than keys | ||||
// left, then too many signatures have failed. | // left, then too many signatures have failed. | ||||
// Exit early, without checking any further | // Exit early, without checking any further | ||||
// signatures. | // signatures. | ||||
if (nSigsRemaining > nKeysRemaining) { | if (nSigsRemaining > nKeysRemaining) { | ||||
fSuccess = false; | fSuccess = false; | ||||
} | } | ||||
} | } | ||||
if (fSuccess && pnSigChecks) { | |||||
// This is not identical to the number of actual | |||||
// ECDSA verifies, but, it is an the upper bound | |||||
markblundebergAuthorUnsubmitted Done Inline Actionsfix "an the" markblundeberg: fix "an the" | |||||
// that can be otherwise determined without | |||||
// doing CPU-intensive checks. | |||||
*pnSigChecks += nKeysCount; | |||||
} | |||||
} | } | ||||
// If the operation failed, we require that all | // If the operation failed, we require that all | ||||
// signatures must be empty vector | // signatures must be empty vector | ||||
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL)) { | if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL)) { | ||||
for (int i = 0; i < nSigsCount; i++) { | for (int i = 0; i < nSigsCount; i++) { | ||||
if (stacktop(-idxTopSig - i).size()) { | if (stacktop(-idxTopSig - i).size()) { | ||||
return set_error(serror, | return set_error(serror, | ||||
▲ Show 20 Lines • Show All 517 Lines • ▼ Show 20 Lines | |||||
// explicit instantiation | // explicit instantiation | ||||
template class GenericTransactionSignatureChecker<CTransaction>; | template class GenericTransactionSignatureChecker<CTransaction>; | ||||
template class GenericTransactionSignatureChecker<CMutableTransaction>; | template class GenericTransactionSignatureChecker<CMutableTransaction>; | ||||
bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, | bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, | ||||
uint32_t flags, const BaseSignatureChecker &checker, | uint32_t flags, const BaseSignatureChecker &checker, | ||||
ScriptError *serror) { | ScriptError *serror) { | ||||
int nSigChecks = 0; | |||||
set_error(serror, ScriptError::UNKNOWN); | set_error(serror, ScriptError::UNKNOWN); | ||||
// If FORKID is enabled, we also ensure strict encoding. | // If FORKID is enabled, we also ensure strict encoding. | ||||
if (flags & SCRIPT_ENABLE_SIGHASH_FORKID) { | if (flags & SCRIPT_ENABLE_SIGHASH_FORKID) { | ||||
flags |= SCRIPT_VERIFY_STRICTENC; | flags |= SCRIPT_VERIFY_STRICTENC; | ||||
} | } | ||||
if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { | if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { | ||||
return set_error(serror, ScriptError::SIG_PUSHONLY); | return set_error(serror, ScriptError::SIG_PUSHONLY); | ||||
} | } | ||||
std::vector<valtype> stack, stackCopy; | std::vector<valtype> stack, stackCopy; | ||||
if (!EvalScript(stack, scriptSig, flags, checker, serror)) { | if (!EvalScript(stack, scriptSig, flags, checker, serror, &nSigChecks)) { | ||||
// serror is set | // serror is set | ||||
return false; | return false; | ||||
} | } | ||||
if (flags & SCRIPT_VERIFY_P2SH) { | if (flags & SCRIPT_VERIFY_P2SH) { | ||||
stackCopy = stack; | stackCopy = stack; | ||||
} | } | ||||
if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { | if (!EvalScript(stack, scriptPubKey, flags, checker, serror, &nSigChecks)) { | ||||
// serror is set | // serror is set | ||||
return false; | return false; | ||||
} | } | ||||
if (stack.empty()) { | if (stack.empty()) { | ||||
return set_error(serror, ScriptError::EVAL_FALSE); | return set_error(serror, ScriptError::EVAL_FALSE); | ||||
} | } | ||||
if (CastToBool(stack.back()) == false) { | if (CastToBool(stack.back()) == false) { | ||||
return set_error(serror, ScriptError::EVAL_FALSE); | return set_error(serror, ScriptError::EVAL_FALSE); | ||||
Show All 21 Lines | if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { | ||||
// Bail out early if SCRIPT_DISALLOW_SEGWIT_RECOVERY is not set, the | // Bail out early if SCRIPT_DISALLOW_SEGWIT_RECOVERY is not set, the | ||||
// redeem script is a p2sh segwit program, and it was the only item | // redeem script is a p2sh segwit program, and it was the only item | ||||
// pushed onto the stack. | // pushed onto the stack. | ||||
if ((flags & SCRIPT_DISALLOW_SEGWIT_RECOVERY) == 0 && stack.empty() && | if ((flags & SCRIPT_DISALLOW_SEGWIT_RECOVERY) == 0 && stack.empty() && | ||||
pubKey2.IsWitnessProgram()) { | pubKey2.IsWitnessProgram()) { | ||||
return set_success(serror); | return set_success(serror); | ||||
} | } | ||||
if (!EvalScript(stack, pubKey2, flags, checker, serror)) { | if (!EvalScript(stack, pubKey2, flags, checker, serror, &nSigChecks)) { | ||||
// serror is set | // serror is set | ||||
return false; | return false; | ||||
} | } | ||||
if (stack.empty()) { | if (stack.empty()) { | ||||
return set_error(serror, ScriptError::EVAL_FALSE); | return set_error(serror, ScriptError::EVAL_FALSE); | ||||
} | } | ||||
if (!CastToBool(stack.back())) { | if (!CastToBool(stack.back())) { | ||||
return set_error(serror, ScriptError::EVAL_FALSE); | return set_error(serror, ScriptError::EVAL_FALSE); | ||||
Show All 9 Lines | if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) { | ||||
// CLEANSTACK->P2SH+CLEANSTACK would be possible, which is not a | // CLEANSTACK->P2SH+CLEANSTACK would be possible, which is not a | ||||
// softfork (and P2SH should be one). | // softfork (and P2SH should be one). | ||||
assert((flags & SCRIPT_VERIFY_P2SH) != 0); | assert((flags & SCRIPT_VERIFY_P2SH) != 0); | ||||
if (stack.size() != 1) { | if (stack.size() != 1) { | ||||
return set_error(serror, ScriptError::CLEANSTACK); | return set_error(serror, ScriptError::CLEANSTACK); | ||||
} | } | ||||
} | } | ||||
if (flags & SCRIPT_RESTRICT_INPUT_SIGCHECKS) { | |||||
// This limit is intended for standard use, and is based on an | |||||
// examination of typical and historical standard uses. | |||||
// - allowing P2SH ECDSA multisig with compressed keys, which at an | |||||
// extreme (1-of-15) may have 15 SigChecks in ~590 bytes of scriptSig. | |||||
// - allowing Bare ECDSA multisig, which at an extreme (1-of-3) may have | |||||
// 3 sigchecks in ~72 bytes of scriptSig. | |||||
// - further restricting SigChecks for very short scriptSigs (<69 | |||||
// bytes), which in normal usage would only have at most one SigCheck. | |||||
// (Historically, six inputs have violated this limit; all were | |||||
// nonstandard scriptPubKeys by today's measure.) | |||||
static_assert(INT_MAX > MAX_SCRIPT_SIZE, | |||||
"overflow sanity check on max script size"); | |||||
static_assert(INT_MAX / 43 / 3 > MAX_OPS_PER_SCRIPT, | |||||
"overflow sanity check on maximum possible sigchecks " | |||||
"from sig+redeem+pub scripts"); | |||||
if (int(scriptSig.size()) < | |||||
std::max(nSigChecks * 23, nSigChecks * 43 - 60)) { | |||||
return set_error(serror, ScriptError::INPUT_SIGCHECKS); | |||||
} | |||||
} | |||||
deadalnixUnsubmitted Not Done Inline ActionsMove all changes to this in another patch. deadalnix: Move all changes to this in another patch. | |||||
return set_success(serror); | return set_success(serror); | ||||
} | } |
Pass a reference to some metric counter object instead of an int*. If you are worried about verbosity on all callsite, add an overload of EvalScript that doesn't need it.