Skip to content

Commit e5cb0df

Browse files
committed
Merge #18002: Abstract out script execution out of VerifyWitnessProgram()
c8e24dd [REFACTOR] Abstract out script execution out of VerifyWitnessProgram() (Pieter Wuille) Pull request description: This is a refactoring cherry-picked out of #17977. As it touches consensus code, I don't think this would ordinarily meet the bar for review cost vs benefit. However, it simplifies the changes for Taproot significantly, and if it's going to be necessitated by inclusion of that code, I may as well give it some additional attention by PRing it independently. ACKs for top commit: fjahr: Re-ACK c8e24dd theStack: re-ACK bitcoin/bitcoin@c8e24dd Empact: Code Review Re-ACK bitcoin/bitcoin@c8e24dd ajtowns: ACK c8e24dd jnewbery: ACK c8e24dd jonatack: ACK c8e24dd Tree-SHA512: 96c2aa5d2f9c7c802bcc008f5cde55b1dfedfaf42e34101331e6c0d594acdf6437661102dc939718f0877c20451336855dfbaa8aa8f57d9e722a7fa7329e3a46
2 parents 3f9e6a3 + c8e24dd commit e5cb0df

File tree

1 file changed

+25
-23
lines changed

1 file changed

+25
-23
lines changed

src/script/interpreter.cpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,9 +1414,26 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
14141414
template class GenericTransactionSignatureChecker<CTransaction>;
14151415
template class GenericTransactionSignatureChecker<CMutableTransaction>;
14161416

1417+
static bool ExecuteWitnessScript(std::vector<valtype>::const_iterator begin, std::vector<valtype>::const_iterator end, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptError* serror)
1418+
{
1419+
std::vector<valtype> stack{begin, end};
1420+
1421+
// Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
1422+
for (const valtype& elem : stack) {
1423+
if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE) return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
1424+
}
1425+
1426+
// Run the script interpreter.
1427+
if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, serror)) return false;
1428+
1429+
// Scripts inside witness implicitly require cleanstack behaviour
1430+
if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK);
1431+
if (!CastToBool(stack.back())) return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
1432+
return true;
1433+
}
1434+
14171435
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
14181436
{
1419-
std::vector<std::vector<unsigned char> > stack;
14201437
CScript scriptPubKey;
14211438

14221439
if (witversion == 0) {
@@ -1426,45 +1443,30 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
14261443
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
14271444
}
14281445
scriptPubKey = CScript(witness.stack.back().begin(), witness.stack.back().end());
1429-
stack = std::vector<std::vector<unsigned char> >(witness.stack.begin(), witness.stack.end() - 1);
14301446
uint256 hashScriptPubKey;
14311447
CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin());
14321448
if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) {
14331449
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
14341450
}
1451+
return ExecuteWitnessScript(witness.stack.begin(), witness.stack.end() - 1, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
14351452
} else if (program.size() == WITNESS_V0_KEYHASH_SIZE) {
14361453
// Special case for pay-to-pubkeyhash; signature + pubkey in witness
14371454
if (witness.stack.size() != 2) {
14381455
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness
14391456
}
14401457
scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
1441-
stack = witness.stack;
1458+
return ExecuteWitnessScript(witness.stack.begin(), witness.stack.end(), scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
14421459
} else {
14431460
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
14441461
}
1445-
} else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
1446-
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
14471462
} else {
1463+
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
1464+
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
1465+
}
14481466
// Higher version witness scripts return true for future softfork compatibility
1449-
return set_success(serror);
1450-
}
1451-
1452-
// Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
1453-
for (unsigned int i = 0; i < stack.size(); i++) {
1454-
if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE)
1455-
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
1456-
}
1457-
1458-
if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::WITNESS_V0, serror)) {
1459-
return false;
1467+
return true;
14601468
}
1461-
1462-
// Scripts inside witness implicitly require cleanstack behaviour
1463-
if (stack.size() != 1)
1464-
return set_error(serror, SCRIPT_ERR_CLEANSTACK);
1465-
if (!CastToBool(stack.back()))
1466-
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
1467-
return true;
1469+
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
14681470
}
14691471

14701472
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)

0 commit comments

Comments
 (0)