bitcoin_script_analyzer/
condition_stack.rs

1// From the Bitcoin Core source code, file src/script/interpreter.cpp at commit b1a2021f78099c17360dc2179cbcb948059b5969
2// Edited for use in this software
3
4// Orignal Bitcoin Core copyright header:
5// Copyright (c) 2009-2010 Satoshi Nakamoto
6// Copyright (c) 2009-2021 The Bitcoin Core developers
7// Distributed under the MIT software license, see the accompanying
8// file COPYING or http://www.opensource.org/licenses/mit-license.php.
9
10/// A data type to abstract out the condition stack during script execution.
11///
12/// Conceptually it acts like a vector of booleans, one for each level of nested
13/// IF/THEN/ELSE, indicating whether we're in the active or inactive branch of
14/// each.
15///
16/// The elements on the stack cannot be observed individually; we only need to
17/// expose whether the stack is empty and whether or not any false values are
18/// present at all. To implement OP_ELSE, a toggle_top modifier is added, which
19/// flips the last value without returning it.
20///
21/// This uses an optimized implementation that does not materialize the
22/// actual stack. Instead, it just stores the size of the would-be stack,
23/// and the position of the first false value in it.
24#[derive(Clone)]
25pub struct ConditionStack {
26    /// The size of the implied stack.
27    m_stack_size: u32,
28    /// The position of the first false value on the implied stack, or NO_FALSE if all true.
29    m_first_false_pos: u32,
30}
31
32impl ConditionStack {
33    /// A constant for m_first_false_pos to indicate there are no falses.
34    const NO_FALSE: u32 = u32::MAX;
35
36    pub fn new() -> Self {
37        Self {
38            m_stack_size: 0,
39            m_first_false_pos: Self::NO_FALSE,
40        }
41    }
42
43    pub fn empty(&self) -> bool {
44        self.m_stack_size == 0
45    }
46
47    pub fn all_true(&self) -> bool {
48        self.m_first_false_pos == Self::NO_FALSE
49    }
50
51    pub fn push_back(&mut self, f: bool) {
52        if self.m_first_false_pos == Self::NO_FALSE && !f {
53            // The stack consists of all true values, and a false is added.
54            // The first false value will appear at the current size.
55            self.m_first_false_pos = self.m_stack_size;
56        }
57        self.m_stack_size += 1;
58    }
59
60    pub fn pop_back(&mut self) {
61        self.m_stack_size -= 1;
62        if self.m_first_false_pos == self.m_stack_size {
63            // When popping off the first false value, everything becomes true.
64            self.m_first_false_pos = Self::NO_FALSE;
65        }
66    }
67
68    pub fn toggle_top(&mut self) {
69        if self.m_first_false_pos == Self::NO_FALSE {
70            // The current stack is all true values; the first false will be the top.
71            self.m_first_false_pos = self.m_stack_size - 1;
72        } else if self.m_first_false_pos == self.m_stack_size - 1 {
73            // The top is the first false value; toggling it will make everything true.
74            self.m_first_false_pos = Self::NO_FALSE;
75        } // else {
76          // There is a false value, but not on top. No action is needed as toggling
77          // anything but the first false value is unobservable.
78          // }
79    }
80}