diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -961,17 +961,15 @@ case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: { - // ([sig ...] num_of_signatures [pubkey ...] + // ([dummy] [sig ...] num_of_signatures [pubkey ...] // num_of_pubkeys -- bool) - - int i = 1; - if ((int)stack.size() < i) { + if (stack.size() < 1) { return set_error( serror, SCRIPT_ERR_INVALID_STACK_OPERATION); } int nKeysCount = - CScriptNum(stacktop(-i), fRequireMinimal).getint(); + CScriptNum(stacktop(-1), fRequireMinimal).getint(); if (nKeysCount < 0 || nKeysCount > MAX_PUBKEYS_PER_MULTISIG) { return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT); @@ -980,41 +978,56 @@ if (nOpCount > MAX_OPS_PER_SCRIPT) { return set_error(serror, SCRIPT_ERR_OP_COUNT); } - int ikey = ++i; - // ikey2 is the position of last non-signature item in - // the stack. Top stack item = 1. With - // SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if - // operation fails. - int ikey2 = nKeysCount + 2; - i += nKeysCount; - if ((int)stack.size() < i) { + // ikey is the position of the last pubkey, if any. + int ikey = 2; + + // isigcount the position of nSigsCount, the stack + // element that divides the signatures and pubkeys list. + // This is used for cleanup. + const int isigcount = nKeysCount + 2; + if (stack.size() < size_t(isigcount)) { return set_error( serror, SCRIPT_ERR_INVALID_STACK_OPERATION); } - int nSigsCount = - CScriptNum(stacktop(-i), fRequireMinimal).getint(); + CScriptNum(stacktop(-isigcount), fRequireMinimal) + .getint(); if (nSigsCount < 0 || nSigsCount > nKeysCount) { return set_error(serror, SCRIPT_ERR_SIG_COUNT); } - int isig = ++i; - i += nSigsCount; - if ((int)stack.size() < i) { + // isig is the position of the last signature, if any. + int isig = nKeysCount + 3; + + // idummy is the position of the dummy element. + const int idummy = nKeysCount + nSigsCount + 3; + if (stack.size() < size_t(idummy)) { return set_error( serror, SCRIPT_ERR_INVALID_STACK_OPERATION); } + bool fSuccess = true; + // Subset of script starting at the most recent // codeseparator CScript scriptCode(pbegincodehash, pend); + // A bug causes CHECKMULTISIG to consume one extra + // argument whose contents were not checked in any way. + // + // Unfortunately this is a potential source of + // mutability, so optionally verify it is exactly equal + // to zero. + if ((flags & SCRIPT_VERIFY_NULLDUMMY) && + stacktop(-idummy).size()) { + return set_error(serror, SCRIPT_ERR_SIG_NULLDUMMY); + } + // Remove signature for pre-fork scripts for (int k = 0; k < nSigsCount; k++) { valtype &vchSig = stacktop(-isig - k); CleanupScriptCode(scriptCode, vchSig, flags); } - bool fSuccess = true; while (fSuccess && nSigsCount > 0) { valtype &vchSig = stacktop(-isig); valtype &vchPubKey = stacktop(-ikey); @@ -1050,39 +1063,26 @@ } } - // Clean up stack of actual arguments - while (i-- > 1) { - // If the operation failed, we require that all - // signatures must be empty vector + // Clean up stack: + // pop nKeysCount, pubkeys, and nSigsCount; + for (int i = 0; i < isigcount; i++) { + popstack(stack); + } + // pop signatures. If the operation failed and NULLFAIL + // flag is set, we require all signatures to be null + // (empty vectors); + for (int i = isigcount + 1; i < idummy; i++) { if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && - !ikey2 && stacktop(-1).size()) { + stacktop(-1).size()) { return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); } - if (ikey2 > 0) { - ikey2--; - } popstack(stack); } - - // A bug causes CHECKMULTISIG to consume one extra - // argument whose contents were not checked in any way. - // - // Unfortunately this is a potential source of - // mutability, so optionally verify it is exactly equal - // to zero prior to removing it from the stack. - if (stack.size() < 1) { - return set_error( - serror, SCRIPT_ERR_INVALID_STACK_OPERATION); - } - if ((flags & SCRIPT_VERIFY_NULLDUMMY) && - stacktop(-1).size()) { - return set_error(serror, SCRIPT_ERR_SIG_NULLDUMMY); - } + // pop dummy element. popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); - if (opcode == OP_CHECKMULTISIGVERIFY) { if (fSuccess) { popstack(stack);