aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/interpreter.cpp70
1 files changed, 67 insertions, 3 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index e4e86031e6..5bf418472a 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -278,6 +278,70 @@ int FindAndDelete(CScript& script, const CScript& b)
return nFound;
}
+namespace {
+/** A data type to abstract out the condition stack during script execution.
+ *
+ * Conceptually it acts like a vector of booleans, one for each level of nested
+ * IF/THEN/ELSE, indicating whether we're in the active or inactive branch of
+ * each.
+ *
+ * The elements on the stack cannot be observed individually; we only need to
+ * expose whether the stack is empty and whether or not any false values are
+ * present at all. To implement OP_ELSE, a toggle_top modifier is added, which
+ * flips the last value without returning it.
+ *
+ * This uses an optimized implementation that does not materialize the
+ * actual stack. Instead, it just stores the size of the would-be stack,
+ * and the position of the first false value in it.
+ */
+class ConditionStack {
+private:
+ //! A constant for m_first_false_pos to indicate there are no falses.
+ static constexpr uint32_t NO_FALSE = std::numeric_limits<uint32_t>::max();
+
+ //! The size of the implied stack.
+ uint32_t m_stack_size = 0;
+ //! The position of the first false value on the implied stack, or NO_FALSE if all true.
+ uint32_t m_first_false_pos = NO_FALSE;
+
+public:
+ bool empty() { return m_stack_size == 0; }
+ bool all_true() { return m_first_false_pos == NO_FALSE; }
+ void push_back(bool f)
+ {
+ if (m_first_false_pos == NO_FALSE && !f) {
+ // The stack consists of all true values, and a false is added.
+ // The first false value will appear at the current size.
+ m_first_false_pos = m_stack_size;
+ }
+ ++m_stack_size;
+ }
+ void pop_back()
+ {
+ assert(m_stack_size > 0);
+ --m_stack_size;
+ if (m_first_false_pos == m_stack_size) {
+ // When popping off the first false value, everything becomes true.
+ m_first_false_pos = NO_FALSE;
+ }
+ }
+ void toggle_top()
+ {
+ assert(m_stack_size > 0);
+ if (m_first_false_pos == NO_FALSE) {
+ // The current stack is all true values; the first false will be the top.
+ m_first_false_pos = m_stack_size - 1;
+ } else if (m_first_false_pos == m_stack_size - 1) {
+ // The top is the first false value; toggling it will make everything true.
+ m_first_false_pos = NO_FALSE;
+ } else {
+ // There is a false value, but not on top. No action is needed as toggling
+ // anything but the first false value is unobservable.
+ }
+ }
+};
+}
+
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
{
static const CScriptNum bnZero(0);
@@ -293,7 +357,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
CScript::const_iterator pbegincodehash = script.begin();
opcodetype opcode;
valtype vchPushValue;
- std::vector<bool> vfExec;
+ ConditionStack vfExec;
std::vector<valtype> altstack;
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if (script.size() > MAX_SCRIPT_SIZE)
@@ -305,7 +369,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
while (pc < pend)
{
- bool fExec = !count(vfExec.begin(), vfExec.end(), false);
+ bool fExec = vfExec.all_true();
//
// Read instruction
@@ -494,7 +558,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
if (vfExec.empty())
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
- vfExec.back() = !vfExec.back();
+ vfExec.toggle_top();
}
break;