Skip to content

Commit 5665504

Browse files
committed
feat(stack): implement control flow
1 parent 337923f commit 5665504

File tree

1 file changed

+306
-5
lines changed

1 file changed

+306
-5
lines changed

stack/src/lib.rs

Lines changed: 306 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use scriptful::{
2-
core::Script,
3-
prelude::{Machine, Stack},
2+
core::{Script, ScriptRef},
3+
prelude::Stack,
44
};
55
use serde::{Deserialize, Serialize};
66

@@ -19,6 +19,12 @@ pub enum MyOperator {
1919
Equal,
2020
Hash160,
2121
CheckMultiSig,
22+
/// Stop script execution if top-most element of stack is not "true"
23+
Verify,
24+
// Control flow
25+
If,
26+
Else,
27+
EndIf,
2228
}
2329

2430
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
@@ -137,11 +143,158 @@ fn check_multi_sig(bytes_pkhs: Vec<MyValue>, bytes_keyed_signatures: Vec<MyValue
137143
}
138144

139145
// An operator system decides what to do with the stack when each operator is applied on it.
140-
fn my_operator_system(stack: &mut Stack<MyValue>, operator: &MyOperator) {
146+
fn my_operator_system(
147+
stack: &mut Stack<MyValue>,
148+
operator: &MyOperator,
149+
if_stack: &mut ConditionStack,
150+
) -> MyControlFlow {
151+
if !if_stack.all_true() {
152+
match operator {
153+
MyOperator::If => {
154+
if_stack.push_back(false);
155+
}
156+
MyOperator::Else => {
157+
if let Err(()) = if_stack.toggle_top() {
158+
stack.push(MyValue::Boolean(false));
159+
return MyControlFlow::Break;
160+
}
161+
}
162+
MyOperator::EndIf => {
163+
if if_stack.pop_back().is_err() {
164+
stack.push(MyValue::Boolean(false));
165+
return MyControlFlow::Break;
166+
}
167+
}
168+
_ => {}
169+
}
170+
171+
return MyControlFlow::Continue;
172+
}
173+
141174
match operator {
142175
MyOperator::Equal => equal_operator(stack),
143176
MyOperator::Hash160 => hash_160_operator(stack),
144177
MyOperator::CheckMultiSig => check_multisig_operator(stack),
178+
MyOperator::Verify => {
179+
let top = stack.pop();
180+
if top != MyValue::Boolean(true) {
181+
// Push the element back because there is a check in execute_script that needs a
182+
// false value to mark the script execution as failed, otherwise it may be marked as
183+
// success
184+
stack.push(top);
185+
return MyControlFlow::Break;
186+
}
187+
}
188+
MyOperator::If => {
189+
let top = stack.pop();
190+
if let MyValue::Boolean(bool) = top {
191+
if_stack.push_back(bool);
192+
} else {
193+
stack.push(MyValue::Boolean(false));
194+
return MyControlFlow::Break;
195+
}
196+
}
197+
MyOperator::Else => {
198+
if let Err(()) = if_stack.toggle_top() {
199+
stack.push(MyValue::Boolean(false));
200+
return MyControlFlow::Break;
201+
}
202+
}
203+
MyOperator::EndIf => {
204+
if if_stack.pop_back().is_err() {
205+
stack.push(MyValue::Boolean(false));
206+
return MyControlFlow::Break;
207+
}
208+
}
209+
}
210+
211+
MyControlFlow::Continue
212+
}
213+
214+
// ConditionStack implementation from bitcoin-core
215+
// https://github.com/bitcoin/bitcoin/blob/505ba3966562b10d6dd4162f3216a120c73a4edb/src/script/interpreter.cpp#L272
216+
// https://bitslog.com/2017/04/17/new-quadratic-delays-in-bitcoin-scripts/
217+
/** A data type to abstract out the condition stack during script execution.
218+
*
219+
* Conceptually it acts like a vector of booleans, one for each level of nested
220+
* IF/THEN/ELSE, indicating whether we're in the active or inactive branch of
221+
* each.
222+
*
223+
* The elements on the stack cannot be observed individually; we only need to
224+
* expose whether the stack is empty and whether or not any false values are
225+
* present at all. To implement OP_ELSE, a toggle_top modifier is added, which
226+
* flips the last value without returning it.
227+
*
228+
* This uses an optimized implementation that does not materialize the
229+
* actual stack. Instead, it just stores the size of the would-be stack,
230+
* and the position of the first false value in it.
231+
*/
232+
pub struct ConditionStack {
233+
stack_size: u32,
234+
first_false_pos: u32,
235+
}
236+
237+
impl Default for ConditionStack {
238+
fn default() -> Self {
239+
Self {
240+
stack_size: 0,
241+
first_false_pos: Self::NO_FALSE,
242+
}
243+
}
244+
}
245+
246+
impl ConditionStack {
247+
const NO_FALSE: u32 = u32::MAX;
248+
249+
pub fn is_empty(&self) -> bool {
250+
self.stack_size == 0
251+
}
252+
253+
pub fn all_true(&self) -> bool {
254+
self.first_false_pos == Self::NO_FALSE
255+
}
256+
257+
pub fn push_back(&mut self, b: bool) {
258+
if (self.first_false_pos == Self::NO_FALSE) && !b {
259+
// The stack consists of all true values, and a false is added.
260+
// The first false value will appear at the current size.
261+
self.first_false_pos = self.stack_size;
262+
}
263+
264+
self.stack_size += 1;
265+
}
266+
267+
pub fn pop_back(&mut self) -> Result<(), ()> {
268+
if self.stack_size == 0 {
269+
return Err(());
270+
}
271+
272+
self.stack_size -= 1;
273+
if self.first_false_pos == self.stack_size {
274+
// When popping off the first false value, everything becomes true.
275+
self.first_false_pos = Self::NO_FALSE;
276+
}
277+
278+
Ok(())
279+
}
280+
281+
pub fn toggle_top(&mut self) -> Result<(), ()> {
282+
if self.stack_size == 0 {
283+
return Err(());
284+
}
285+
286+
if self.first_false_pos == Self::NO_FALSE {
287+
// The current stack is all true values; the first false will be the top.
288+
self.first_false_pos = self.stack_size - 1;
289+
} else if self.first_false_pos == self.stack_size - 1 {
290+
// The top is the first false value; toggling it will make everything true.
291+
self.first_false_pos = Self::NO_FALSE;
292+
} else {
293+
// There is a false value, but not on top. No action is needed as toggling
294+
// anything but the first false value is unobservable.
295+
}
296+
297+
Ok(())
145298
}
146299
}
147300

@@ -194,10 +347,10 @@ pub fn encode(a: Script<MyOperator, MyValue>) -> Vec<u8> {
194347

195348
fn execute_script(script: Script<MyOperator, MyValue>) -> bool {
196349
// Instantiate the machine with a reference to your operator system.
197-
let mut machine = Machine::new(&my_operator_system);
350+
let mut machine = Machine2::new(&my_operator_system);
198351
let result = machine.run_script(&script);
199352

200-
result == Some(&MyValue::Boolean(true))
353+
result == None || result == Some(&MyValue::Boolean(true))
201354
}
202355

203356
fn execute_locking_script(redeem_bytes: &[u8], locking_bytes: &[u8; 20]) -> bool {
@@ -240,6 +393,67 @@ pub fn execute_complete_script(
240393
execute_redeem_script(witness_bytes, redeem_bytes)
241394
}
242395

396+
// TODO: use control flow enum from scriptful library when ready
397+
pub enum MyControlFlow {
398+
Continue,
399+
Break,
400+
}
401+
402+
pub struct Machine2<'a, Op, Val>
403+
where
404+
Val: core::fmt::Debug + core::cmp::PartialEq,
405+
{
406+
op_sys: &'a dyn Fn(&mut Stack<Val>, &Op, &mut ConditionStack) -> MyControlFlow,
407+
stack: Stack<Val>,
408+
if_stack: ConditionStack,
409+
}
410+
411+
impl<'a, Op, Val> Machine2<'a, Op, Val>
412+
where
413+
Op: core::fmt::Debug + core::cmp::Eq,
414+
Val: core::fmt::Debug + core::cmp::PartialEq + core::clone::Clone,
415+
{
416+
pub fn new(
417+
op_sys: &'a dyn Fn(&mut Stack<Val>, &Op, &mut ConditionStack) -> MyControlFlow,
418+
) -> Self {
419+
Self {
420+
op_sys,
421+
stack: Stack::<Val>::default(),
422+
if_stack: ConditionStack::default(),
423+
}
424+
}
425+
426+
pub fn operate(&mut self, item: &Item<Op, Val>) -> MyControlFlow {
427+
match item {
428+
Item::Operator(operator) => {
429+
(self.op_sys)(&mut self.stack, operator, &mut self.if_stack)
430+
}
431+
Item::Value(value) => {
432+
if self.if_stack.all_true() {
433+
self.stack.push((*value).clone());
434+
}
435+
436+
MyControlFlow::Continue
437+
}
438+
}
439+
}
440+
441+
pub fn run_script(&mut self, script: ScriptRef<Op, Val>) -> Option<&Val> {
442+
for item in script {
443+
match self.operate(item) {
444+
MyControlFlow::Continue => {
445+
continue;
446+
}
447+
MyControlFlow::Break => {
448+
break;
449+
}
450+
}
451+
}
452+
453+
self.stack.topmost()
454+
}
455+
}
456+
243457
#[cfg(test)]
244458
mod tests {
245459
use super::*;
@@ -414,4 +628,91 @@ mod tests {
414628
&encode(redeem_script)
415629
));
416630
}
631+
632+
#[test]
633+
fn test_execute_script_op_verify() {
634+
let s = vec![
635+
Item::Value(MyValue::String("patata".to_string())),
636+
Item::Value(MyValue::String("patata".to_string())),
637+
Item::Operator(MyOperator::Equal),
638+
Item::Operator(MyOperator::Verify),
639+
];
640+
assert!(execute_script(s));
641+
642+
let s = vec![
643+
Item::Value(MyValue::String("patata".to_string())),
644+
Item::Value(MyValue::String("potato".to_string())),
645+
Item::Operator(MyOperator::Equal),
646+
Item::Operator(MyOperator::Verify),
647+
];
648+
assert!(!execute_script(s));
649+
}
650+
651+
#[test]
652+
fn test_execute_script_op_if() {
653+
let s = vec![
654+
Item::Value(MyValue::String("patata".to_string())),
655+
Item::Value(MyValue::Boolean(true)),
656+
Item::Operator(MyOperator::If),
657+
Item::Value(MyValue::String("patata".to_string())),
658+
Item::Operator(MyOperator::Else),
659+
Item::Value(MyValue::String("potato".to_string())),
660+
Item::Operator(MyOperator::EndIf),
661+
Item::Operator(MyOperator::Equal),
662+
Item::Operator(MyOperator::Verify),
663+
];
664+
assert!(execute_script(s));
665+
666+
let s = vec![
667+
Item::Value(MyValue::String("patata".to_string())),
668+
Item::Value(MyValue::Boolean(false)),
669+
Item::Operator(MyOperator::If),
670+
Item::Value(MyValue::String("patata".to_string())),
671+
Item::Operator(MyOperator::Else),
672+
Item::Value(MyValue::String("potato".to_string())),
673+
Item::Operator(MyOperator::EndIf),
674+
Item::Operator(MyOperator::Equal),
675+
Item::Operator(MyOperator::Verify),
676+
];
677+
assert!(!execute_script(s));
678+
}
679+
680+
#[test]
681+
fn test_execute_script_op_if_nested() {
682+
let s = vec![
683+
Item::Value(MyValue::String("patata".to_string())),
684+
Item::Value(MyValue::Boolean(true)),
685+
Item::Operator(MyOperator::If),
686+
Item::Value(MyValue::String("patata".to_string())),
687+
Item::Operator(MyOperator::Else),
688+
Item::Value(MyValue::Boolean(false)),
689+
Item::Operator(MyOperator::If),
690+
Item::Value(MyValue::String("patata".to_string())),
691+
Item::Operator(MyOperator::Else),
692+
Item::Value(MyValue::String("potato".to_string())),
693+
Item::Operator(MyOperator::EndIf),
694+
Item::Operator(MyOperator::EndIf),
695+
Item::Operator(MyOperator::Equal),
696+
Item::Operator(MyOperator::Verify),
697+
];
698+
assert!(execute_script(s));
699+
700+
let s = vec![
701+
Item::Value(MyValue::String("potato".to_string())),
702+
Item::Value(MyValue::Boolean(false)),
703+
Item::Operator(MyOperator::If),
704+
Item::Value(MyValue::String("patata".to_string())),
705+
Item::Operator(MyOperator::Else),
706+
Item::Value(MyValue::Boolean(false)),
707+
Item::Operator(MyOperator::If),
708+
Item::Value(MyValue::String("patata".to_string())),
709+
Item::Operator(MyOperator::Else),
710+
Item::Value(MyValue::String("potato".to_string())),
711+
Item::Operator(MyOperator::EndIf),
712+
Item::Operator(MyOperator::EndIf),
713+
Item::Operator(MyOperator::Equal),
714+
Item::Operator(MyOperator::Verify),
715+
];
716+
assert!(execute_script(s));
717+
}
417718
}

0 commit comments

Comments
 (0)