1use std::collections::{HashMap, HashSet};
16
17use crate::ir::{ANFBinding, ANFMethod, ANFProgram, ANFProperty, ANFValue, ConstValue};
18
19const MAX_STACK_DEPTH: usize = 800;
24
25#[derive(Debug, Clone)]
31pub enum StackOp {
32 Push(PushValue),
33 Dup,
34 Swap,
35 Roll { depth: usize },
36 Pick { depth: usize },
37 Drop,
38 Nip,
39 Over,
40 Rot,
41 Tuck,
42 Opcode(String),
43 If {
44 then_ops: Vec<StackOp>,
45 else_ops: Vec<StackOp>,
46 },
47 Placeholder {
48 param_index: usize,
49 param_name: String,
50 },
51}
52
53#[derive(Debug, Clone)]
55pub enum PushValue {
56 Bool(bool),
57 Int(i128),
58 Bytes(Vec<u8>),
59}
60
61#[derive(Debug, Clone)]
63pub struct StackMethod {
64 pub name: String,
65 pub ops: Vec<StackOp>,
66 pub max_stack_depth: usize,
67}
68
69fn is_ec_builtin(name: &str) -> bool {
74 matches!(
75 name,
76 "ecAdd"
77 | "ecMul"
78 | "ecMulGen"
79 | "ecNegate"
80 | "ecOnCurve"
81 | "ecModReduce"
82 | "ecEncodeCompressed"
83 | "ecMakePoint"
84 | "ecPointX"
85 | "ecPointY"
86 )
87}
88
89fn builtin_opcodes(name: &str) -> Option<Vec<&'static str>> {
90 match name {
91 "sha256" => Some(vec!["OP_SHA256"]),
92 "ripemd160" => Some(vec!["OP_RIPEMD160"]),
93 "hash160" => Some(vec!["OP_HASH160"]),
94 "hash256" => Some(vec!["OP_HASH256"]),
95 "checkSig" => Some(vec!["OP_CHECKSIG"]),
96 "checkMultiSig" => Some(vec!["OP_CHECKMULTISIG"]),
97 "len" => Some(vec!["OP_SIZE"]),
98 "cat" => Some(vec!["OP_CAT"]),
99 "num2bin" => Some(vec!["OP_NUM2BIN"]),
100 "bin2num" => Some(vec!["OP_BIN2NUM"]),
101 "abs" => Some(vec!["OP_ABS"]),
102 "min" => Some(vec!["OP_MIN"]),
103 "max" => Some(vec!["OP_MAX"]),
104 "within" => Some(vec!["OP_WITHIN"]),
105 "split" => Some(vec!["OP_SPLIT"]),
106 "left" => Some(vec!["OP_SPLIT", "OP_DROP"]),
107 "int2str" => Some(vec!["OP_NUM2BIN"]),
108 "bool" => Some(vec!["OP_0NOTEQUAL"]),
109 "unpack" => Some(vec!["OP_BIN2NUM"]),
110 _ => None,
111 }
112}
113
114fn binop_opcodes(op: &str) -> Option<Vec<&'static str>> {
115 match op {
116 "+" => Some(vec!["OP_ADD"]),
117 "-" => Some(vec!["OP_SUB"]),
118 "*" => Some(vec!["OP_MUL"]),
119 "/" => Some(vec!["OP_DIV"]),
120 "%" => Some(vec!["OP_MOD"]),
121 "===" => Some(vec!["OP_NUMEQUAL"]),
122 "!==" => Some(vec!["OP_NUMEQUAL", "OP_NOT"]),
123 "<" => Some(vec!["OP_LESSTHAN"]),
124 ">" => Some(vec!["OP_GREATERTHAN"]),
125 "<=" => Some(vec!["OP_LESSTHANOREQUAL"]),
126 ">=" => Some(vec!["OP_GREATERTHANOREQUAL"]),
127 "&&" => Some(vec!["OP_BOOLAND"]),
128 "||" => Some(vec!["OP_BOOLOR"]),
129 "&" => Some(vec!["OP_AND"]),
130 "|" => Some(vec!["OP_OR"]),
131 "^" => Some(vec!["OP_XOR"]),
132 "<<" => Some(vec!["OP_LSHIFT"]),
133 ">>" => Some(vec!["OP_RSHIFT"]),
134 _ => None,
135 }
136}
137
138fn unaryop_opcodes(op: &str) -> Option<Vec<&'static str>> {
139 match op {
140 "!" => Some(vec!["OP_NOT"]),
141 "-" => Some(vec!["OP_NEGATE"]),
142 "~" => Some(vec!["OP_INVERT"]),
143 _ => None,
144 }
145}
146
147#[derive(Debug, Clone)]
154struct StackMap {
155 slots: Vec<String>,
156}
157
158impl StackMap {
159 fn new(initial: &[String]) -> Self {
160 StackMap {
161 slots: initial.to_vec(),
162 }
163 }
164
165 fn depth(&self) -> usize {
166 self.slots.len()
167 }
168
169 fn push(&mut self, name: &str) {
170 self.slots.push(name.to_string());
171 }
172
173 fn pop(&mut self) -> String {
174 self.slots.pop().expect("stack underflow")
175 }
176
177 fn find_depth(&self, name: &str) -> Option<usize> {
178 for (i, slot) in self.slots.iter().enumerate().rev() {
179 if slot == name {
180 return Some(self.slots.len() - 1 - i);
181 }
182 }
183 None
184 }
185
186 fn has(&self, name: &str) -> bool {
187 self.slots.iter().any(|s| s == name)
188 }
189
190 fn remove_at_depth(&mut self, depth_from_top: usize) -> String {
191 let index = self.slots.len() - 1 - depth_from_top;
192 self.slots.remove(index)
193 }
194
195 fn peek_at_depth(&self, depth_from_top: usize) -> &str {
196 let index = self.slots.len() - 1 - depth_from_top;
197 &self.slots[index]
198 }
199
200 fn rename_at_depth(&mut self, depth_from_top: usize, new_name: &str) {
201 let idx = self.slots.len() - 1 - depth_from_top;
202 self.slots[idx] = new_name.to_string();
203 }
204
205 fn swap(&mut self) {
206 let n = self.slots.len();
207 assert!(n >= 2, "stack underflow on swap");
208 self.slots.swap(n - 1, n - 2);
209 }
210
211 fn dup(&mut self) {
212 assert!(!self.slots.is_empty(), "stack underflow on dup");
213 let top = self.slots.last().unwrap().clone();
214 self.slots.push(top);
215 }
216
217 fn named_slots(&self) -> HashSet<String> {
219 self.slots.iter().filter(|s| !s.is_empty()).cloned().collect()
220 }
221}
222
223fn compute_last_uses(bindings: &[ANFBinding]) -> HashMap<String, usize> {
228 let mut last_use = HashMap::new();
229 for (i, binding) in bindings.iter().enumerate() {
230 for r in collect_refs(&binding.value) {
231 last_use.insert(r, i);
232 }
233 }
234 last_use
235}
236
237fn collect_refs(value: &ANFValue) -> Vec<String> {
238 let mut refs = Vec::new();
239 match value {
240 ANFValue::LoadParam { name } => {
241 refs.push(name.clone());
244 }
245 ANFValue::LoadProp { .. }
246 | ANFValue::GetStateScript { .. } => {}
247
248 ANFValue::LoadConst { value: v } => {
249 if let Some(s) = v.as_str() {
251 if s.len() > 5 && &s[..5] == "@ref:" {
252 refs.push(s[5..].to_string());
253 }
254 }
255 }
256
257 ANFValue::BinOp { left, right, .. } => {
258 refs.push(left.clone());
259 refs.push(right.clone());
260 }
261 ANFValue::UnaryOp { operand, .. } => {
262 refs.push(operand.clone());
263 }
264 ANFValue::Call { args, .. } => {
265 refs.extend(args.iter().cloned());
266 }
267 ANFValue::MethodCall { object, args, .. } => {
268 refs.push(object.clone());
269 refs.extend(args.iter().cloned());
270 }
271 ANFValue::If {
272 cond,
273 then,
274 else_branch,
275 } => {
276 refs.push(cond.clone());
277 for b in then {
278 refs.extend(collect_refs(&b.value));
279 }
280 for b in else_branch {
281 refs.extend(collect_refs(&b.value));
282 }
283 }
284 ANFValue::Loop { body, .. } => {
285 for b in body {
286 refs.extend(collect_refs(&b.value));
287 }
288 }
289 ANFValue::Assert { value } => {
290 refs.push(value.clone());
291 }
292 ANFValue::UpdateProp { value, .. } => {
293 refs.push(value.clone());
294 }
295 ANFValue::CheckPreimage { preimage } => {
296 refs.push(preimage.clone());
297 }
298 ANFValue::DeserializeState { preimage } => {
299 refs.push(preimage.clone());
300 }
301 ANFValue::AddOutput { satoshis, state_values, preimage } => {
302 refs.push(satoshis.clone());
303 refs.extend(state_values.iter().cloned());
304 if !preimage.is_empty() {
305 refs.push(preimage.clone());
306 }
307 }
308 ANFValue::AddRawOutput { satoshis, script_bytes } => {
309 refs.push(satoshis.clone());
310 refs.push(script_bytes.clone());
311 }
312 ANFValue::ArrayLiteral { elements } => {
313 refs.extend(elements.iter().cloned());
314 }
315 }
316 refs
317}
318
319struct LoweringContext {
324 sm: StackMap,
325 ops: Vec<StackOp>,
326 max_depth: usize,
327 properties: Vec<ANFProperty>,
328 private_methods: HashMap<String, ANFMethod>,
329 local_bindings: HashSet<String>,
332 outer_protected_refs: Option<HashSet<String>>,
334 inside_branch: bool,
337}
338
339impl LoweringContext {
340 fn new(params: &[String], properties: &[ANFProperty]) -> Self {
341 let mut ctx = LoweringContext {
342 sm: StackMap::new(params),
343 ops: Vec::new(),
344 max_depth: 0,
345 properties: properties.to_vec(),
346 private_methods: HashMap::new(),
347 local_bindings: HashSet::new(),
348 outer_protected_refs: None,
349 inside_branch: false,
350 };
351 ctx.track_depth();
352 ctx
353 }
354
355 fn track_depth(&mut self) {
356 if self.sm.depth() > self.max_depth {
357 self.max_depth = self.sm.depth();
358 }
359 }
360
361 fn emit_op(&mut self, op: StackOp) {
362 self.ops.push(op);
363 self.track_depth();
364 }
365
366 fn emit_varint_encoding(&mut self) {
376 self.emit_op(StackOp::Dup); self.sm.dup();
379 self.emit_op(StackOp::Push(PushValue::Int(253))); self.sm.push("");
381 self.emit_op(StackOp::Opcode("OP_LESSTHAN".into())); self.sm.pop();
383 self.sm.pop();
384 self.sm.push("");
385
386 self.emit_op(StackOp::Opcode("OP_IF".into()));
387 self.sm.pop(); self.emit_op(StackOp::Push(PushValue::Int(2))); self.sm.push("");
394 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into())); self.sm.pop();
396 self.sm.pop();
397 self.sm.push("");
398 self.emit_op(StackOp::Push(PushValue::Int(1))); self.sm.push("");
400 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
402 self.sm.pop();
403 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
407
408 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
409
410 self.emit_op(StackOp::Push(PushValue::Int(4))); self.sm.push("");
415 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into())); self.sm.pop();
417 self.sm.pop();
418 self.sm.push("");
419 self.emit_op(StackOp::Push(PushValue::Int(2))); self.sm.push("");
421 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
423 self.sm.pop();
424 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
428 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0xfd])));
429 self.sm.push("");
430 self.emit_op(StackOp::Swap);
431 self.sm.swap();
432 self.sm.pop();
433 self.sm.pop();
434 self.emit_op(StackOp::Opcode("OP_CAT".into()));
435 self.sm.push("");
436
437 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
438 }
440
441 fn is_last_use(&self, name: &str, current_index: usize, last_uses: &HashMap<String, usize>) -> bool {
442 match last_uses.get(name) {
443 None => true,
444 Some(&last) => last <= current_index,
445 }
446 }
447
448 fn bring_to_top(&mut self, name: &str, consume: bool) {
449 let depth = self
450 .sm
451 .find_depth(name)
452 .unwrap_or_else(|| panic!("value '{}' not found on stack", name));
453
454 if depth == 0 {
455 if !consume {
456 self.emit_op(StackOp::Dup);
457 self.sm.dup();
458 }
459 return;
460 }
461
462 if depth == 1 && consume {
463 self.emit_op(StackOp::Swap);
464 self.sm.swap();
465 return;
466 }
467
468 if consume {
469 if depth == 2 {
470 self.emit_op(StackOp::Rot);
471 let removed = self.sm.remove_at_depth(2);
472 self.sm.push(&removed);
473 } else {
474 self.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
475 self.sm.push(""); self.emit_op(StackOp::Roll { depth });
477 self.sm.pop(); let rolled = self.sm.remove_at_depth(depth);
479 self.sm.push(&rolled);
480 }
481 } else {
482 if depth == 1 {
483 self.emit_op(StackOp::Over);
484 let picked = self.sm.peek_at_depth(1).to_string();
485 self.sm.push(&picked);
486 } else {
487 self.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
488 self.sm.push(""); self.emit_op(StackOp::Pick { depth });
490 self.sm.pop(); let picked = self.sm.peek_at_depth(depth).to_string();
492 self.sm.push(&picked);
493 }
494 }
495
496 self.track_depth();
497 }
498
499 fn lower_bindings(&mut self, bindings: &[ANFBinding], terminal_assert: bool) {
504 self.local_bindings = bindings.iter().map(|b| b.name.clone()).collect();
505 let mut last_uses = compute_last_uses(bindings);
506
507 if let Some(ref protected) = self.outer_protected_refs {
509 for r in protected {
510 last_uses.insert(r.clone(), bindings.len());
511 }
512 }
513
514 let mut last_assert_idx: isize = -1;
518 let mut terminal_if_idx: isize = -1;
519 if terminal_assert {
520 let last_binding = bindings.last();
521 if let Some(b) = last_binding {
522 if matches!(&b.value, ANFValue::If { .. }) {
523 terminal_if_idx = (bindings.len() - 1) as isize;
524 } else {
525 for i in (0..bindings.len()).rev() {
526 if matches!(&bindings[i].value, ANFValue::Assert { .. }) {
527 last_assert_idx = i as isize;
528 break;
529 }
530 }
531 }
532 }
533 }
534
535 for (i, binding) in bindings.iter().enumerate() {
536 if matches!(&binding.value, ANFValue::Assert { .. }) && i as isize == last_assert_idx {
537 if let ANFValue::Assert { value } = &binding.value {
539 self.lower_assert(value, i, &last_uses, true);
540 }
541 } else if matches!(&binding.value, ANFValue::If { .. }) && i as isize == terminal_if_idx {
542 if let ANFValue::If { cond, then, else_branch } = &binding.value {
544 self.lower_if(&binding.name, cond, then, else_branch, i, &last_uses, true);
545 }
546 } else {
547 self.lower_binding(binding, i, &last_uses);
548 }
549 }
550 }
551
552 fn lower_binding(
553 &mut self,
554 binding: &ANFBinding,
555 binding_index: usize,
556 last_uses: &HashMap<String, usize>,
557 ) {
558 let name = &binding.name;
559 match &binding.value {
560 ANFValue::LoadParam {
561 name: param_name, ..
562 } => {
563 self.lower_load_param(name, param_name, binding_index, last_uses);
564 }
565 ANFValue::LoadProp {
566 name: prop_name, ..
567 } => {
568 self.lower_load_prop(name, prop_name);
569 }
570 ANFValue::LoadConst { .. } => {
571 self.lower_load_const(name, &binding.value, binding_index, last_uses);
572 }
573 ANFValue::BinOp {
574 op, left, right, result_type, ..
575 } => {
576 self.lower_bin_op(name, op, left, right, binding_index, last_uses, result_type.as_deref());
577 }
578 ANFValue::UnaryOp { op, operand, .. } => {
579 self.lower_unary_op(name, op, operand, binding_index, last_uses);
580 }
581 ANFValue::Call {
582 func: func_name,
583 args,
584 } => {
585 self.lower_call(name, func_name, args, binding_index, last_uses);
586 }
587 ANFValue::MethodCall {
588 object,
589 method,
590 args,
591 } => {
592 self.lower_method_call(name, object, method, args, binding_index, last_uses);
593 }
594 ANFValue::If {
595 cond,
596 then,
597 else_branch,
598 } => {
599 self.lower_if(name, cond, then, else_branch, binding_index, last_uses, false);
600 }
601 ANFValue::Loop {
602 count,
603 body,
604 iter_var,
605 } => {
606 self.lower_loop(name, *count, body, iter_var);
607 }
608 ANFValue::Assert { value } => {
609 self.lower_assert(value, binding_index, last_uses, false);
610 }
611 ANFValue::UpdateProp {
612 name: prop_name,
613 value,
614 } => {
615 self.lower_update_prop(prop_name, value, binding_index, last_uses);
616 }
617 ANFValue::GetStateScript {} => {
618 self.lower_get_state_script(name);
619 }
620 ANFValue::CheckPreimage { preimage } => {
621 self.lower_check_preimage(name, preimage, binding_index, last_uses);
622 }
623 ANFValue::DeserializeState { preimage } => {
624 self.lower_deserialize_state(preimage, binding_index, last_uses);
625 }
626 ANFValue::AddOutput { satoshis, state_values, preimage } => {
627 self.lower_add_output(name, satoshis, state_values, preimage, binding_index, last_uses);
628 }
629 ANFValue::AddRawOutput { satoshis, script_bytes } => {
630 self.lower_add_raw_output(name, satoshis, script_bytes, binding_index, last_uses);
631 }
632 ANFValue::ArrayLiteral { elements } => {
633 self.lower_array_literal(name, elements, binding_index, last_uses);
634 }
635 }
636 }
637
638 fn lower_load_param(
643 &mut self,
644 binding_name: &str,
645 param_name: &str,
646 binding_index: usize,
647 last_uses: &HashMap<String, usize>,
648 ) {
649 if self.sm.has(param_name) {
650 let is_last = self.is_last_use(param_name, binding_index, last_uses);
651 self.bring_to_top(param_name, is_last);
652 self.sm.pop();
653 self.sm.push(binding_name);
654 } else {
655 self.emit_op(StackOp::Push(PushValue::Int(0)));
656 self.sm.push(binding_name);
657 }
658 }
659
660 fn lower_load_prop(&mut self, binding_name: &str, prop_name: &str) {
661 let prop = self.properties.iter().find(|p| p.name == prop_name).cloned();
662
663 if self.sm.has(prop_name) {
664 self.bring_to_top(prop_name, false);
668 self.sm.pop();
669 } else if let Some(ref p) = prop {
670 if let Some(ref val) = p.initial_value {
671 self.push_json_value(val);
672 } else {
673 let param_index = self
676 .properties
677 .iter()
678 .position(|p2| p2.name == prop_name)
679 .unwrap_or(0);
680 self.emit_op(StackOp::Placeholder {
681 param_index,
682 param_name: prop_name.to_string(),
683 });
684 }
685 } else {
686 let param_index = self
688 .properties
689 .iter()
690 .position(|p2| p2.name == prop_name)
691 .unwrap_or(0);
692 self.emit_op(StackOp::Placeholder {
693 param_index,
694 param_name: prop_name.to_string(),
695 });
696 }
697 self.sm.push(binding_name);
698 }
699
700 fn push_json_value(&mut self, val: &serde_json::Value) {
701 match val {
702 serde_json::Value::Bool(b) => {
703 self.emit_op(StackOp::Push(PushValue::Bool(*b)));
704 }
705 serde_json::Value::Number(n) => {
706 let i = n.as_i64().map(|v| v as i128).unwrap_or(0);
707 self.emit_op(StackOp::Push(PushValue::Int(i)));
708 }
709 serde_json::Value::String(s) => {
710 let bytes = hex_to_bytes(s);
711 self.emit_op(StackOp::Push(PushValue::Bytes(bytes)));
712 }
713 _ => {
714 self.emit_op(StackOp::Push(PushValue::Int(0)));
715 }
716 }
717 }
718
719 fn lower_load_const(&mut self, binding_name: &str, value: &ANFValue, binding_index: usize, last_uses: &HashMap<String, usize>) {
720 if let Some(ConstValue::Str(ref s)) = value.const_value() {
725 if s.len() > 5 && &s[..5] == "@ref:" {
726 let ref_name = &s[5..];
727 if self.sm.has(ref_name) {
728 let consume = self.local_bindings.contains(ref_name)
733 && self.is_last_use(ref_name, binding_index, last_uses);
734 self.bring_to_top(ref_name, consume);
735 self.sm.pop();
736 self.sm.push(binding_name);
737 } else {
738 self.emit_op(StackOp::Push(PushValue::Int(0)));
740 self.sm.push(binding_name);
741 }
742 return;
743 }
744 if s == "@this" {
746 self.emit_op(StackOp::Push(PushValue::Int(0)));
747 self.sm.push(binding_name);
748 return;
749 }
750 }
751
752 match value.const_value() {
753 Some(ConstValue::Bool(b)) => {
754 self.emit_op(StackOp::Push(PushValue::Bool(b)));
755 }
756 Some(ConstValue::Int(n)) => {
757 self.emit_op(StackOp::Push(PushValue::Int(n)));
758 }
759 Some(ConstValue::Str(s)) => {
760 let bytes = hex_to_bytes(&s);
761 self.emit_op(StackOp::Push(PushValue::Bytes(bytes)));
762 }
763 None => {
764 self.emit_op(StackOp::Push(PushValue::Int(0)));
765 }
766 }
767 self.sm.push(binding_name);
768 }
769
770 fn lower_bin_op(
771 &mut self,
772 binding_name: &str,
773 op: &str,
774 left: &str,
775 right: &str,
776 binding_index: usize,
777 last_uses: &HashMap<String, usize>,
778 result_type: Option<&str>,
779 ) {
780 let left_is_last = self.is_last_use(left, binding_index, last_uses);
781 self.bring_to_top(left, left_is_last);
782
783 let right_is_last = self.is_last_use(right, binding_index, last_uses);
784 self.bring_to_top(right, right_is_last);
785
786 self.sm.pop();
787 self.sm.pop();
788
789 if result_type == Some("bytes") && op == "+" {
792 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
793 } else if result_type == Some("bytes") && (op == "===" || op == "!==") {
794 self.emit_op(StackOp::Opcode("OP_EQUAL".to_string()));
795 if op == "!==" {
796 self.emit_op(StackOp::Opcode("OP_NOT".to_string()));
797 }
798 } else {
799 let codes = binop_opcodes(op)
800 .unwrap_or_else(|| panic!("unknown binary operator: {}", op));
801 for code in codes {
802 self.emit_op(StackOp::Opcode(code.to_string()));
803 }
804 }
805
806 self.sm.push(binding_name);
807 self.track_depth();
808 }
809
810 fn lower_unary_op(
811 &mut self,
812 binding_name: &str,
813 op: &str,
814 operand: &str,
815 binding_index: usize,
816 last_uses: &HashMap<String, usize>,
817 ) {
818 let is_last = self.is_last_use(operand, binding_index, last_uses);
819 self.bring_to_top(operand, is_last);
820
821 self.sm.pop();
822
823 let codes = unaryop_opcodes(op)
824 .unwrap_or_else(|| panic!("unknown unary operator: {}", op));
825 for code in codes {
826 self.emit_op(StackOp::Opcode(code.to_string()));
827 }
828
829 self.sm.push(binding_name);
830 self.track_depth();
831 }
832
833 fn lower_call(
834 &mut self,
835 binding_name: &str,
836 func_name: &str,
837 args: &[String],
838 binding_index: usize,
839 last_uses: &HashMap<String, usize>,
840 ) {
841 if func_name == "assert" {
843 if !args.is_empty() {
844 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
845 self.bring_to_top(&args[0], is_last);
846 self.sm.pop();
847 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
848 self.sm.push(binding_name);
849 }
850 return;
851 }
852
853 if func_name == "super" {
856 self.sm.push(binding_name);
857 return;
858 }
859
860 if func_name == "checkMultiSig" && args.len() == 2 {
862 self.lower_check_multi_sig(binding_name, args, binding_index, last_uses);
863 return;
864 }
865
866 if func_name == "__array_access" {
867 self.lower_array_access(binding_name, args, binding_index, last_uses);
868 return;
869 }
870
871 if func_name == "reverseBytes" {
872 self.lower_reverse_bytes(binding_name, args, binding_index, last_uses);
873 return;
874 }
875
876 if func_name == "substr" {
877 self.lower_substr(binding_name, args, binding_index, last_uses);
878 return;
879 }
880
881 if func_name == "verifyRabinSig" {
882 self.lower_verify_rabin_sig(binding_name, args, binding_index, last_uses);
883 return;
884 }
885
886 if func_name == "verifyWOTS" {
887 self.lower_verify_wots(binding_name, args, binding_index, last_uses);
888 return;
889 }
890
891 if func_name.starts_with("verifySLHDSA_") {
892 let param_key = func_name.trim_start_matches("verifySLHDSA_");
893 self.lower_verify_slh_dsa(binding_name, param_key, args, binding_index, last_uses);
894 return;
895 }
896
897 if func_name == "sha256Compress" {
898 self.lower_sha256_compress(binding_name, args, binding_index, last_uses);
899 return;
900 }
901
902 if func_name == "sha256Finalize" {
903 self.lower_sha256_finalize(binding_name, args, binding_index, last_uses);
904 return;
905 }
906
907 if func_name == "blake3Compress" {
908 self.lower_blake3_compress(binding_name, args, binding_index, last_uses);
909 return;
910 }
911
912 if func_name == "blake3Hash" {
913 self.lower_blake3_hash(binding_name, args, binding_index, last_uses);
914 return;
915 }
916
917 if is_ec_builtin(func_name) {
918 self.lower_ec_builtin(binding_name, func_name, args, binding_index, last_uses);
919 return;
920 }
921
922 if func_name == "safediv" {
923 self.lower_safediv(binding_name, args, binding_index, last_uses);
924 return;
925 }
926
927 if func_name == "safemod" {
928 self.lower_safemod(binding_name, args, binding_index, last_uses);
929 return;
930 }
931
932 if func_name == "clamp" {
933 self.lower_clamp(binding_name, args, binding_index, last_uses);
934 return;
935 }
936
937 if func_name == "pow" {
938 self.lower_pow(binding_name, args, binding_index, last_uses);
939 return;
940 }
941
942 if func_name == "mulDiv" {
943 self.lower_mul_div(binding_name, args, binding_index, last_uses);
944 return;
945 }
946
947 if func_name == "percentOf" {
948 self.lower_percent_of(binding_name, args, binding_index, last_uses);
949 return;
950 }
951
952 if func_name == "sqrt" {
953 self.lower_sqrt(binding_name, args, binding_index, last_uses);
954 return;
955 }
956
957 if func_name == "gcd" {
958 self.lower_gcd(binding_name, args, binding_index, last_uses);
959 return;
960 }
961
962 if func_name == "divmod" {
963 self.lower_divmod(binding_name, args, binding_index, last_uses);
964 return;
965 }
966
967 if func_name == "log2" {
968 self.lower_log2(binding_name, args, binding_index, last_uses);
969 return;
970 }
971
972 if func_name == "sign" {
973 self.lower_sign(binding_name, args, binding_index, last_uses);
974 return;
975 }
976
977 if func_name == "right" {
978 self.lower_right(binding_name, args, binding_index, last_uses);
979 return;
980 }
981
982 if func_name == "pack" || func_name == "toByteString" {
985 if !args.is_empty() {
986 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
987 self.bring_to_top(&args[0], is_last);
988 self.sm.pop();
989 }
990 self.sm.push(binding_name);
991 return;
992 }
993
994 if func_name == "computeStateOutputHash" {
997 self.lower_compute_state_output_hash(binding_name, args, binding_index, last_uses);
998 return;
999 }
1000
1001 if func_name == "computeStateOutput" {
1005 self.lower_compute_state_output(binding_name, args, binding_index, last_uses);
1006 return;
1007 }
1008
1009 if func_name == "buildChangeOutput" {
1013 self.lower_build_change_output(binding_name, args, binding_index, last_uses);
1014 return;
1015 }
1016
1017 if func_name.starts_with("extract") {
1021 self.lower_extractor(binding_name, func_name, args, binding_index, last_uses);
1022 return;
1023 }
1024
1025 for arg in args {
1027 let is_last = self.is_last_use(arg, binding_index, last_uses);
1028 self.bring_to_top(arg, is_last);
1029 }
1030
1031 for _ in args {
1032 self.sm.pop();
1033 }
1034
1035 if let Some(codes) = builtin_opcodes(func_name) {
1036 for code in codes {
1037 self.emit_op(StackOp::Opcode(code.to_string()));
1038 }
1039 } else {
1040 self.emit_op(StackOp::Push(PushValue::Int(0)));
1042 self.sm.push(binding_name);
1043 return;
1044 }
1045
1046 if func_name == "split" {
1047 self.sm.push("");
1048 self.sm.push(binding_name);
1049 } else if func_name == "len" {
1050 self.sm.push("");
1051 self.sm.push(binding_name);
1052 } else {
1053 self.sm.push(binding_name);
1054 }
1055
1056 self.track_depth();
1057 }
1058
1059 fn lower_method_call(
1060 &mut self,
1061 binding_name: &str,
1062 object: &str,
1063 method: &str,
1064 args: &[String],
1065 binding_index: usize,
1066 last_uses: &HashMap<String, usize>,
1067 ) {
1068 if self.sm.has(object) {
1071 self.bring_to_top(object, true);
1072 self.emit_op(StackOp::Drop);
1073 self.sm.pop();
1074 }
1075
1076 if method == "getStateScript" {
1077 self.lower_get_state_script(binding_name);
1078 return;
1079 }
1080
1081 if let Some(private_method) = self.private_methods.get(method).cloned() {
1083 self.inline_method_call(binding_name, &private_method, args, binding_index, last_uses);
1084 return;
1085 }
1086
1087 self.lower_call(binding_name, method, args, binding_index, last_uses);
1089 }
1090
1091 fn inline_method_call(
1094 &mut self,
1095 binding_name: &str,
1096 method: &ANFMethod,
1097 args: &[String],
1098 binding_index: usize,
1099 last_uses: &HashMap<String, usize>,
1100 ) {
1101 let mut shadowed: Vec<(String, String)> = Vec::new();
1106
1107 for (i, arg) in args.iter().enumerate() {
1109 if i < method.params.len() {
1110 let is_last = self.is_last_use(arg, binding_index, last_uses);
1111 self.bring_to_top(arg, is_last);
1112 self.sm.pop();
1113
1114 let param_name = &method.params[i].name;
1115
1116 if self.sm.has(param_name) {
1119 let existing_depth = self.sm.find_depth(param_name).unwrap();
1120 let shadowed_name = format!("__shadowed_{}_{}", binding_index, param_name);
1121 self.sm.rename_at_depth(existing_depth, &shadowed_name);
1122 shadowed.push((param_name.clone(), shadowed_name));
1123 }
1124
1125 self.sm.push(param_name);
1127 }
1128 }
1129
1130 self.lower_bindings(&method.body, false);
1132
1133 for (param_name, shadowed_name) in &shadowed {
1135 if self.sm.has(shadowed_name) {
1136 let depth = self.sm.find_depth(shadowed_name).unwrap();
1137 self.sm.rename_at_depth(depth, param_name);
1138 }
1139 }
1140
1141 if !method.body.is_empty() {
1144 let last_binding_name = &method.body[method.body.len() - 1].name;
1145 if self.sm.depth() > 0 {
1146 let top_name = self.sm.peek_at_depth(0).to_string();
1147 if top_name == *last_binding_name {
1148 self.sm.pop();
1149 self.sm.push(binding_name);
1150 }
1151 }
1152 }
1153 }
1154
1155 fn lower_if(
1156 &mut self,
1157 binding_name: &str,
1158 cond: &str,
1159 then_bindings: &[ANFBinding],
1160 else_bindings: &[ANFBinding],
1161 binding_index: usize,
1162 last_uses: &HashMap<String, usize>,
1163 terminal_assert: bool,
1164 ) {
1165 let is_last = self.is_last_use(cond, binding_index, last_uses);
1166 self.bring_to_top(cond, is_last);
1167 self.sm.pop(); let mut protected_refs = HashSet::new();
1171 for (ref_name, &last_idx) in last_uses.iter() {
1172 if last_idx > binding_index && self.sm.has(ref_name) {
1173 protected_refs.insert(ref_name.clone());
1174 }
1175 }
1176
1177 let pre_if_names = self.sm.named_slots();
1179
1180 let mut then_ctx = LoweringContext::new(&[], &self.properties);
1182 then_ctx.sm = self.sm.clone();
1183 then_ctx.outer_protected_refs = Some(protected_refs.clone());
1184 then_ctx.inside_branch = true;
1185 then_ctx.lower_bindings(then_bindings, terminal_assert);
1186
1187 if terminal_assert && then_ctx.sm.depth() > 1 {
1188 let excess = then_ctx.sm.depth() - 1;
1189 for _ in 0..excess {
1190 then_ctx.emit_op(StackOp::Nip);
1191 then_ctx.sm.remove_at_depth(1);
1192 }
1193 }
1194
1195 let mut else_ctx = LoweringContext::new(&[], &self.properties);
1197 else_ctx.sm = self.sm.clone();
1198 else_ctx.outer_protected_refs = Some(protected_refs);
1199 else_ctx.inside_branch = true;
1200 else_ctx.lower_bindings(else_bindings, terminal_assert);
1201
1202 if terminal_assert && else_ctx.sm.depth() > 1 {
1203 let excess = else_ctx.sm.depth() - 1;
1204 for _ in 0..excess {
1205 else_ctx.emit_op(StackOp::Nip);
1206 else_ctx.sm.remove_at_depth(1);
1207 }
1208 }
1209
1210 let post_then_names = then_ctx.sm.named_slots();
1223 let mut consumed_names: Vec<String> = Vec::new();
1224 for name in &pre_if_names {
1225 if !post_then_names.contains(name) && else_ctx.sm.has(name) {
1226 consumed_names.push(name.clone());
1227 }
1228 }
1229 let post_else_names = else_ctx.sm.named_slots();
1230 let mut else_consumed_names: Vec<String> = Vec::new();
1231 for name in &pre_if_names {
1232 if !post_else_names.contains(name) && then_ctx.sm.has(name) {
1233 else_consumed_names.push(name.clone());
1234 }
1235 }
1236
1237 if !consumed_names.is_empty() {
1240 let mut depths: Vec<usize> = consumed_names
1241 .iter()
1242 .map(|n| else_ctx.sm.find_depth(n).unwrap())
1243 .collect();
1244 depths.sort_by(|a, b| b.cmp(a));
1245 for depth in depths {
1246 if depth == 0 {
1247 else_ctx.emit_op(StackOp::Drop);
1248 else_ctx.sm.pop();
1249 } else if depth == 1 {
1250 else_ctx.emit_op(StackOp::Nip);
1251 else_ctx.sm.remove_at_depth(1);
1252 } else {
1253 else_ctx.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
1254 else_ctx.sm.push("");
1255 else_ctx.emit_op(StackOp::Roll { depth });
1256 else_ctx.sm.pop();
1257 let rolled = else_ctx.sm.remove_at_depth(depth);
1258 else_ctx.sm.push(&rolled);
1259 else_ctx.emit_op(StackOp::Drop);
1260 else_ctx.sm.pop();
1261 }
1262 }
1263 }
1264 if !else_consumed_names.is_empty() {
1265 let mut depths: Vec<usize> = else_consumed_names
1266 .iter()
1267 .map(|n| then_ctx.sm.find_depth(n).unwrap())
1268 .collect();
1269 depths.sort_by(|a, b| b.cmp(a));
1270 for depth in depths {
1271 if depth == 0 {
1272 then_ctx.emit_op(StackOp::Drop);
1273 then_ctx.sm.pop();
1274 } else if depth == 1 {
1275 then_ctx.emit_op(StackOp::Nip);
1276 then_ctx.sm.remove_at_depth(1);
1277 } else {
1278 then_ctx.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
1279 then_ctx.sm.push("");
1280 then_ctx.emit_op(StackOp::Roll { depth });
1281 then_ctx.sm.pop();
1282 let rolled = then_ctx.sm.remove_at_depth(depth);
1283 then_ctx.sm.push(&rolled);
1284 then_ctx.emit_op(StackOp::Drop);
1285 then_ctx.sm.pop();
1286 }
1287 }
1288 }
1289
1290 if then_ctx.sm.depth() > else_ctx.sm.depth() {
1293 let then_top_p3 = then_ctx.sm.peek_at_depth(0).to_string();
1297 if else_bindings.is_empty() && !then_top_p3.is_empty() && else_ctx.sm.has(&then_top_p3) {
1298 let var_depth = else_ctx.sm.find_depth(&then_top_p3).unwrap();
1299 if var_depth == 0 {
1300 else_ctx.emit_op(StackOp::Dup);
1301 } else {
1302 else_ctx.emit_op(StackOp::Push(PushValue::Int(var_depth as i128)));
1303 else_ctx.sm.push("");
1304 else_ctx.emit_op(StackOp::Pick { depth: var_depth });
1305 else_ctx.sm.pop();
1306 }
1307 else_ctx.sm.push(&then_top_p3);
1308 } else {
1309 else_ctx.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1310 else_ctx.sm.push("");
1311 }
1312 } else if else_ctx.sm.depth() > then_ctx.sm.depth() {
1313 then_ctx.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1314 then_ctx.sm.push("");
1315 }
1316
1317 let then_ops = then_ctx.ops;
1318 let else_ops = else_ctx.ops;
1319
1320 self.emit_op(StackOp::If {
1321 then_ops,
1322 else_ops: if else_ops.is_empty() {
1323 Vec::new()
1324 } else {
1325 else_ops
1326 },
1327 });
1328
1329 let post_branch_names = then_ctx.sm.named_slots();
1331 for name in &pre_if_names {
1332 if !post_branch_names.contains(name) && self.sm.has(name) {
1333 if let Some(depth) = self.sm.find_depth(name) {
1334 self.sm.remove_at_depth(depth);
1335 }
1336 }
1337 }
1338
1339 if then_ctx.sm.depth() > self.sm.depth() {
1341 let then_top = then_ctx.sm.peek_at_depth(0).to_string();
1342 let else_top = if else_ctx.sm.depth() > 0 {
1343 else_ctx.sm.peek_at_depth(0).to_string()
1344 } else {
1345 String::new()
1346 };
1347 let is_property = self.properties.iter().any(|p| p.name == then_top);
1348 if is_property && !then_top.is_empty() && then_top == else_top
1349 && then_top != binding_name && self.sm.has(&then_top)
1350 {
1351 self.sm.push(&then_top);
1353 for d in 1..self.sm.depth() {
1354 if self.sm.peek_at_depth(d) == then_top {
1355 if d == 1 {
1356 self.emit_op(StackOp::Nip);
1357 self.sm.remove_at_depth(1);
1358 } else {
1359 self.emit_op(StackOp::Push(PushValue::Int(d as i128)));
1360 self.sm.push("");
1361 self.emit_op(StackOp::Roll { depth: d + 1 });
1362 self.sm.pop();
1363 let rolled = self.sm.remove_at_depth(d);
1364 self.sm.push(&rolled);
1365 self.emit_op(StackOp::Drop);
1366 self.sm.pop();
1367 }
1368 break;
1369 }
1370 }
1371 } else if !then_top.is_empty() && !is_property && else_bindings.is_empty()
1372 && then_top != binding_name && self.sm.has(&then_top)
1373 {
1374 self.sm.push(&then_top);
1378 for d in 1..self.sm.depth() {
1379 if self.sm.peek_at_depth(d) == then_top {
1380 if d == 1 {
1381 self.emit_op(StackOp::Nip);
1382 self.sm.remove_at_depth(1);
1383 } else {
1384 self.emit_op(StackOp::Push(PushValue::Int(d as i128)));
1385 self.sm.push("");
1386 self.emit_op(StackOp::Roll { depth: d + 1 });
1387 self.sm.pop();
1388 let rolled = self.sm.remove_at_depth(d);
1389 self.sm.push(&rolled);
1390 self.emit_op(StackOp::Drop);
1391 self.sm.pop();
1392 }
1393 break;
1394 }
1395 }
1396 } else {
1397 self.sm.push(binding_name);
1398 }
1399 } else if else_ctx.sm.depth() > self.sm.depth() {
1400 self.sm.push(binding_name);
1401 } else {
1402 }
1404 self.track_depth();
1405
1406 if then_ctx.max_depth > self.max_depth {
1407 self.max_depth = then_ctx.max_depth;
1408 }
1409 if else_ctx.max_depth > self.max_depth {
1410 self.max_depth = else_ctx.max_depth;
1411 }
1412 }
1413
1414 fn lower_loop(
1415 &mut self,
1416 _binding_name: &str,
1417 count: usize,
1418 body: &[ANFBinding],
1419 iter_var: &str,
1420 ) {
1421 let body_binding_names: HashSet<String> = body.iter().map(|b| b.name.clone()).collect();
1424 let mut outer_refs = HashSet::new();
1425 for b in body {
1426 if let ANFValue::LoadParam { name } = &b.value {
1427 if name != iter_var {
1428 outer_refs.insert(name.clone());
1429 }
1430 }
1431 if let ANFValue::LoadConst { value: v } = &b.value {
1433 if let Some(s) = v.as_str() {
1434 if s.len() > 5 && &s[..5] == "@ref:" {
1435 let ref_name = &s[5..];
1436 if !body_binding_names.contains(ref_name) {
1437 outer_refs.insert(ref_name.to_string());
1438 }
1439 }
1440 }
1441 }
1442 }
1443
1444 let prev_local_bindings = self.local_bindings.clone();
1447 self.local_bindings = self.local_bindings.union(&body_binding_names).cloned().collect();
1448
1449 for i in 0..count {
1450 self.emit_op(StackOp::Push(PushValue::Int(i as i128)));
1451 self.sm.push(iter_var);
1452
1453 let mut last_uses = compute_last_uses(body);
1454
1455 if i < count - 1 {
1458 for ref_name in &outer_refs {
1459 last_uses.insert(ref_name.clone(), body.len());
1460 }
1461 }
1462
1463 for (j, binding) in body.iter().enumerate() {
1464 self.lower_binding(binding, j, &last_uses);
1465 }
1466
1467 if self.sm.has(iter_var) {
1470 let depth = self.sm.find_depth(iter_var);
1471 if let Some(0) = depth {
1472 self.emit_op(StackOp::Drop);
1473 self.sm.pop();
1474 }
1475 }
1476 }
1477 self.local_bindings = prev_local_bindings;
1479 }
1483
1484 fn lower_assert(
1485 &mut self,
1486 value_ref: &str,
1487 binding_index: usize,
1488 last_uses: &HashMap<String, usize>,
1489 terminal: bool,
1490 ) {
1491 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1492 self.bring_to_top(value_ref, is_last);
1493 if terminal {
1494 } else {
1497 self.sm.pop();
1498 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
1499 }
1500 self.track_depth();
1501 }
1502
1503 fn lower_update_prop(
1504 &mut self,
1505 prop_name: &str,
1506 value_ref: &str,
1507 binding_index: usize,
1508 last_uses: &HashMap<String, usize>,
1509 ) {
1510 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1511 self.bring_to_top(value_ref, is_last);
1512 self.sm.pop();
1513 self.sm.push(prop_name);
1514
1515 if !self.inside_branch {
1522 for d in 1..self.sm.depth() {
1523 if self.sm.peek_at_depth(d) == prop_name {
1524 if d == 1 {
1525 self.emit_op(StackOp::Nip);
1526 self.sm.remove_at_depth(1);
1527 } else {
1528 self.emit_op(StackOp::Push(PushValue::Int(d as i128)));
1529 self.sm.push("");
1530 self.emit_op(StackOp::Roll { depth: d + 1 });
1531 self.sm.pop();
1532 let rolled = self.sm.remove_at_depth(d);
1533 self.sm.push(&rolled);
1534 self.emit_op(StackOp::Drop);
1535 self.sm.pop();
1536 }
1537 break;
1538 }
1539 }
1540 }
1541
1542 self.track_depth();
1543 }
1544
1545 fn lower_get_state_script(&mut self, binding_name: &str) {
1546 let state_props: Vec<ANFProperty> = self
1547 .properties
1548 .iter()
1549 .filter(|p| !p.readonly)
1550 .cloned()
1551 .collect();
1552
1553 if state_props.is_empty() {
1554 self.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1555 self.sm.push(binding_name);
1556 return;
1557 }
1558
1559 let mut first = true;
1560 for prop in &state_props {
1561 if self.sm.has(&prop.name) {
1562 self.bring_to_top(&prop.name, true); } else if let Some(ref val) = prop.initial_value {
1564 self.push_json_value(val);
1565 self.sm.push("");
1566 } else {
1567 self.emit_op(StackOp::Push(PushValue::Int(0)));
1568 self.sm.push("");
1569 }
1570
1571 if prop.prop_type == "bigint" {
1573 self.emit_op(StackOp::Push(PushValue::Int(8)));
1574 self.sm.push("");
1575 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1576 self.sm.pop(); } else if prop.prop_type == "boolean" {
1578 self.emit_op(StackOp::Push(PushValue::Int(1)));
1579 self.sm.push("");
1580 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1581 self.sm.pop(); }
1583 if !first {
1586 self.sm.pop();
1587 self.sm.pop();
1588 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
1589 self.sm.push("");
1590 }
1591 first = false;
1592 }
1593
1594 self.sm.pop();
1595 self.sm.push(binding_name);
1596 self.track_depth();
1597 }
1598
1599 fn lower_compute_state_output_hash(
1603 &mut self,
1604 binding_name: &str,
1605 args: &[String],
1606 binding_index: usize,
1607 last_uses: &std::collections::HashMap<String, usize>,
1608 ) {
1609 let preimage_ref = &args[0];
1610 let state_bytes_ref = &args[1];
1611
1612 let sb_last = self.is_last_use(state_bytes_ref, binding_index, last_uses);
1614 self.bring_to_top(state_bytes_ref, sb_last);
1615
1616 let pre_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1618 self.bring_to_top(preimage_ref, pre_last);
1619
1620 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1622 self.sm.push("");
1623 self.emit_op(StackOp::Push(PushValue::Int(52))); self.sm.push("");
1625 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1626 self.sm.pop();
1627 self.sm.pop();
1628 self.sm.push("");
1629 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
1631 self.sm.pop();
1632 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip); self.sm.pop();
1636 self.sm.pop();
1637 self.sm.push("");
1638 self.emit_op(StackOp::Push(PushValue::Int(8)));
1639 self.sm.push("");
1640 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
1642 self.sm.pop();
1643 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
1647 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
1651 self.sm.pop();
1652
1653 self.bring_to_top("_codePart", false);
1655 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1659 self.sm.push("");
1660 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1661 self.sm.pop();
1662 self.sm.pop();
1663 self.sm.push("");
1664 self.emit_op(StackOp::Swap);
1667 self.sm.swap();
1668 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1669 self.sm.pop();
1670 self.sm.pop();
1671 self.sm.push("");
1672 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1676 self.sm.push("");
1677 self.emit_varint_encoding();
1678
1679 self.emit_op(StackOp::Swap);
1681 self.sm.swap();
1682 self.sm.pop();
1683 self.sm.pop();
1684 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1685 self.sm.push("");
1686
1687 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
1689 self.sm.push("");
1690 self.emit_op(StackOp::Swap);
1691 self.sm.swap();
1692 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1693 self.sm.pop();
1694 self.sm.pop();
1695 self.sm.push("");
1696
1697 self.emit_op(StackOp::Opcode("OP_HASH256".into()));
1699
1700 self.sm.pop();
1701 self.sm.push(binding_name);
1702 self.track_depth();
1703 }
1704
1705 fn lower_compute_state_output(
1709 &mut self,
1710 binding_name: &str,
1711 args: &[String],
1712 binding_index: usize,
1713 last_uses: &std::collections::HashMap<String, usize>,
1714 ) {
1715 let preimage_ref = &args[0];
1716 let state_bytes_ref = &args[1];
1717 let new_amount_ref = &args[2];
1718
1719 let pre_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1721 self.bring_to_top(preimage_ref, pre_last);
1722 self.emit_op(StackOp::Drop);
1723 self.sm.pop();
1724
1725 let amount_last = self.is_last_use(new_amount_ref, binding_index, last_uses);
1727 self.bring_to_top(new_amount_ref, amount_last);
1728 self.emit_op(StackOp::Push(PushValue::Int(8)));
1729 self.sm.push("");
1730 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
1731 self.sm.pop();
1732 self.sm.pop();
1733 self.sm.push("");
1734 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
1735 self.sm.pop();
1736
1737 let sb_last = self.is_last_use(state_bytes_ref, binding_index, last_uses);
1739 self.bring_to_top(state_bytes_ref, sb_last);
1740
1741 self.bring_to_top("_codePart", false);
1743 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1747 self.sm.push("");
1748 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1749 self.sm.pop();
1750 self.sm.pop();
1751 self.sm.push("");
1752 self.emit_op(StackOp::Swap);
1755 self.sm.swap();
1756 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1757 self.sm.pop();
1758 self.sm.pop();
1759 self.sm.push("");
1760 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1764 self.sm.push("");
1765 self.emit_varint_encoding();
1766
1767 self.emit_op(StackOp::Swap);
1769 self.sm.swap();
1770 self.sm.pop();
1771 self.sm.pop();
1772 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1773 self.sm.push("");
1774
1775 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
1777 self.sm.push("");
1778 self.emit_op(StackOp::Swap);
1779 self.sm.swap();
1780 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1781 self.sm.pop();
1782 self.sm.pop();
1783 self.sm.push("");
1784 self.sm.pop();
1787 self.sm.push(binding_name);
1788 self.track_depth();
1789 }
1790
1791 fn lower_build_change_output(
1795 &mut self,
1796 binding_name: &str,
1797 args: &[String],
1798 binding_index: usize,
1799 last_uses: &std::collections::HashMap<String, usize>,
1800 ) {
1801 let pkh_ref = &args[0];
1802 let amount_ref = &args[1];
1803
1804 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x19, 0x76, 0xa9, 0x14])));
1807 self.sm.push("");
1808
1809 let pkh_last = self.is_last_use(pkh_ref, binding_index, last_uses);
1811 self.bring_to_top(pkh_ref, pkh_last);
1812 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1814 self.sm.pop();
1815 self.sm.pop();
1816 self.sm.push("");
1817
1818 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x88, 0xac])));
1820 self.sm.push("");
1821 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1823 self.sm.pop();
1824 self.sm.pop();
1825 self.sm.push("");
1826 let amount_last = self.is_last_use(amount_ref, binding_index, last_uses);
1830 self.bring_to_top(amount_ref, amount_last);
1831 self.emit_op(StackOp::Push(PushValue::Int(8)));
1832 self.sm.push("");
1833 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
1834 self.sm.pop(); self.emit_op(StackOp::Swap);
1837 self.sm.swap();
1838 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1840 self.sm.pop();
1841 self.sm.pop();
1842 self.sm.push("");
1843 self.sm.pop();
1846 self.sm.push(binding_name);
1847 self.track_depth();
1848 }
1849
1850 fn lower_add_output(
1851 &mut self,
1852 binding_name: &str,
1853 satoshis: &str,
1854 state_values: &[String],
1855 _preimage: &str,
1856 binding_index: usize,
1857 last_uses: &HashMap<String, usize>,
1858 ) {
1859 let state_props: Vec<ANFProperty> = self
1865 .properties
1866 .iter()
1867 .filter(|p| !p.readonly)
1868 .cloned()
1869 .collect();
1870
1871 self.bring_to_top("_codePart", false);
1873 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1877 self.sm.push("");
1878 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1879 self.sm.pop();
1880 self.sm.pop();
1881 self.sm.push("");
1882 for (i, value_ref) in state_values.iter().enumerate() {
1886 if i >= state_props.len() {
1887 break;
1888 }
1889 let prop = &state_props[i];
1890
1891 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1892 self.bring_to_top(value_ref, is_last);
1893
1894 if prop.prop_type == "bigint" {
1895 self.emit_op(StackOp::Push(PushValue::Int(8)));
1896 self.sm.push("");
1897 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1898 self.sm.pop();
1899 } else if prop.prop_type == "boolean" {
1900 self.emit_op(StackOp::Push(PushValue::Int(1)));
1901 self.sm.push("");
1902 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1903 self.sm.pop();
1904 }
1905
1906 self.sm.pop();
1907 self.sm.pop();
1908 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
1909 self.sm.push("");
1910 }
1911 self.emit_op(StackOp::Opcode("OP_SIZE".into())); self.sm.push("");
1916 self.emit_varint_encoding();
1917 self.emit_op(StackOp::Swap);
1921 self.sm.swap();
1922 self.sm.pop();
1923 self.sm.pop();
1924 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1925 self.sm.push("");
1926 let is_last_satoshis = self.is_last_use(satoshis, binding_index, last_uses);
1930 self.bring_to_top(satoshis, is_last_satoshis);
1931 self.emit_op(StackOp::Push(PushValue::Int(8)));
1932 self.sm.push("");
1933 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1934 self.sm.pop(); self.emit_op(StackOp::Swap);
1937 self.sm.swap();
1938 self.sm.pop();
1939 self.sm.pop();
1940 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
1942 self.sm.pop();
1946 self.sm.push(binding_name);
1947 self.track_depth();
1948 }
1949
1950 fn lower_add_raw_output(
1954 &mut self,
1955 binding_name: &str,
1956 satoshis: &str,
1957 script_bytes: &str,
1958 binding_index: usize,
1959 last_uses: &HashMap<String, usize>,
1960 ) {
1961 let script_is_last = self.is_last_use(script_bytes, binding_index, last_uses);
1963 self.bring_to_top(script_bytes, script_is_last);
1964
1965 self.emit_op(StackOp::Opcode("OP_SIZE".to_string())); self.sm.push("");
1968 self.emit_varint_encoding();
1969 self.emit_op(StackOp::Swap); self.sm.swap();
1974 self.sm.pop();
1975 self.sm.pop();
1976 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
1978
1979 let sat_is_last = self.is_last_use(satoshis, binding_index, last_uses);
1981 self.bring_to_top(satoshis, sat_is_last);
1982 self.emit_op(StackOp::Push(PushValue::Int(8)));
1983 self.sm.push("");
1984 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1985 self.sm.pop(); self.emit_op(StackOp::Swap);
1988 self.sm.swap();
1989 self.sm.pop();
1990 self.sm.pop();
1991 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
1993
1994 self.sm.pop();
1996 self.sm.push(binding_name);
1997 self.track_depth();
1998 }
1999
2000 fn lower_array_literal(
2001 &mut self,
2002 binding_name: &str,
2003 elements: &[String],
2004 binding_index: usize,
2005 last_uses: &HashMap<String, usize>,
2006 ) {
2007 for elem in elements {
2011 let is_last = self.is_last_use(elem, binding_index, last_uses);
2012 self.bring_to_top(elem, is_last);
2013 self.sm.pop();
2014 self.sm.push(""); }
2016 if !elements.is_empty() {
2018 self.sm.pop();
2019 }
2020 self.sm.push(binding_name);
2021 self.track_depth();
2022 }
2023
2024 fn lower_check_multi_sig(
2025 &mut self,
2026 binding_name: &str,
2027 args: &[String],
2028 binding_index: usize,
2029 last_uses: &HashMap<String, usize>,
2030 ) {
2031 self.emit_op(StackOp::Push(PushValue::Int(0)));
2040 self.sm.push("");
2041
2042 let sigs_is_last = self.is_last_use(&args[0], binding_index, last_uses);
2044 self.bring_to_top(&args[0], sigs_is_last);
2045
2046 let pks_is_last = self.is_last_use(&args[1], binding_index, last_uses);
2048 self.bring_to_top(&args[1], pks_is_last);
2049
2050 self.sm.pop(); self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_CHECKMULTISIG".to_string()));
2056 self.sm.push(binding_name);
2057 self.track_depth();
2058 }
2059
2060 fn lower_check_preimage(
2061 &mut self,
2062 binding_name: &str,
2063 preimage: &str,
2064 binding_index: usize,
2065 last_uses: &HashMap<String, usize>,
2066 ) {
2067 self.emit_op(StackOp::Opcode("OP_CODESEPARATOR".to_string()));
2103
2104 let is_last = self.is_last_use(preimage, binding_index, last_uses);
2106 self.bring_to_top(preimage, is_last);
2107
2108 self.bring_to_top("_opPushTxSig", true);
2110
2111 let g: Vec<u8> = vec![
2113 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB,
2114 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B,
2115 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28,
2116 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17,
2117 0x98,
2118 ];
2119 self.emit_op(StackOp::Push(PushValue::Bytes(g)));
2120 self.sm.push(""); self.emit_op(StackOp::Opcode("OP_CHECKSIG".to_string()));
2124 self.sm.pop(); self.sm.pop(); self.sm.push(""); self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
2130 self.sm.pop(); self.sm.pop();
2135 self.sm.push(binding_name);
2136
2137 self.track_depth();
2138 }
2139
2140 fn lower_deserialize_state(
2149 &mut self,
2150 preimage_ref: &str,
2151 binding_index: usize,
2152 last_uses: &HashMap<String, usize>,
2153 ) {
2154 let mut prop_names: Vec<String> = Vec::new();
2158 let mut prop_types: Vec<String> = Vec::new();
2159 let mut prop_sizes: Vec<i128> = Vec::new();
2160 let mut state_len: i128 = 0;
2161
2162 for p in &self.properties {
2163 if p.readonly {
2164 continue;
2165 }
2166 prop_names.push(p.name.clone());
2167 prop_types.push(p.prop_type.clone());
2168 let sz: i128 = match p.prop_type.as_str() {
2169 "bigint" => 8,
2170 "boolean" => 1,
2171 "PubKey" => 33,
2172 "Addr" => 20,
2173 "Sha256" => 32,
2174 "Point" => 64,
2175 _ => panic!("deserialize_state: unsupported type: {}", p.prop_type),
2176 };
2177 prop_sizes.push(sz);
2178 state_len += sz;
2179 }
2180
2181 if prop_names.is_empty() {
2182 return;
2183 }
2184
2185 let is_last = self.is_last_use(preimage_ref, binding_index, last_uses);
2187 self.bring_to_top(preimage_ref, is_last);
2188
2189 self.emit_op(StackOp::Push(PushValue::Int(104)));
2191 self.sm.push("");
2192 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2193 self.sm.pop();
2194 self.sm.pop();
2195 self.sm.push("");
2196 self.sm.push("");
2197 self.emit_op(StackOp::Nip);
2198 self.sm.pop();
2199 self.sm.pop();
2200 self.sm.push("");
2201
2202 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2204 self.sm.push("");
2205 self.emit_op(StackOp::Push(PushValue::Int(44)));
2206 self.sm.push("");
2207 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2208 self.sm.pop();
2209 self.sm.pop();
2210 self.sm.push("");
2211 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2212 self.sm.pop();
2213 self.sm.pop();
2214 self.sm.push("");
2215 self.sm.push("");
2216 self.emit_op(StackOp::Drop);
2217 self.sm.pop();
2218
2219 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2221 self.sm.push("");
2222 self.emit_op(StackOp::Push(PushValue::Int(8)));
2223 self.sm.push("");
2224 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2225 self.sm.pop();
2226 self.sm.pop();
2227 self.sm.push("");
2228 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2229 self.sm.pop();
2230 self.sm.pop();
2231 self.sm.push("");
2232 self.sm.push("");
2233 self.emit_op(StackOp::Drop);
2234 self.sm.pop();
2235
2236 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2238 self.sm.push("");
2239 self.emit_op(StackOp::Push(PushValue::Int(state_len)));
2240 self.sm.push("");
2241 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2242 self.sm.pop();
2243 self.sm.pop();
2244 self.sm.push("");
2245 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2246 self.sm.pop();
2247 self.sm.pop();
2248 self.sm.push("");
2249 self.sm.push("");
2250 self.emit_op(StackOp::Nip);
2251 self.sm.pop();
2252 self.sm.pop();
2253 self.sm.push("");
2254
2255 let num_props = prop_names.len();
2257
2258 if num_props == 1 {
2259 if prop_types[0] == "bigint" || prop_types[0] == "boolean" {
2261 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2262 }
2263 self.sm.pop();
2264 self.sm.push(&prop_names[0]);
2265 } else {
2266 for i in 0..num_props {
2268 let sz = prop_sizes[i];
2269 if i < num_props - 1 {
2270 self.emit_op(StackOp::Push(PushValue::Int(sz)));
2272 self.sm.push("");
2273 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2274 self.sm.pop();
2275 self.sm.pop();
2276 self.sm.push("");
2277 self.sm.push("");
2278 self.emit_op(StackOp::Swap);
2280 self.sm.swap();
2281 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2283 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2284 }
2285 self.emit_op(StackOp::Swap);
2287 self.sm.swap();
2288 self.sm.pop();
2289 self.sm.pop();
2290 self.sm.push(&prop_names[i]);
2291 self.sm.push("");
2292 } else {
2293 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2295 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2296 }
2297 self.sm.pop();
2298 self.sm.push(&prop_names[i]);
2299 }
2300 }
2301 }
2302
2303 self.track_depth();
2304 }
2305
2306 fn lower_extractor(
2324 &mut self,
2325 binding_name: &str,
2326 func_name: &str,
2327 args: &[String],
2328 binding_index: usize,
2329 last_uses: &HashMap<String, usize>,
2330 ) {
2331 assert!(!args.is_empty(), "{} requires 1 argument", func_name);
2332 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
2333 self.bring_to_top(&args[0], is_last);
2334
2335 self.sm.pop(); match func_name {
2339 "extractVersion" => {
2340 self.emit_op(StackOp::Push(PushValue::Int(4)));
2342 self.sm.push("");
2343 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2344 self.sm.pop();
2345 self.sm.push("");
2346 self.sm.push("");
2347 self.emit_op(StackOp::Drop);
2348 self.sm.pop();
2349 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2350 }
2351 "extractHashPrevouts" => {
2352 self.emit_op(StackOp::Push(PushValue::Int(4)));
2354 self.sm.push("");
2355 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2356 self.sm.pop();
2357 self.sm.push("");
2358 self.sm.push("");
2359 self.emit_op(StackOp::Nip);
2360 self.sm.pop();
2361 self.sm.pop();
2362 self.sm.push("");
2363 self.emit_op(StackOp::Push(PushValue::Int(32)));
2364 self.sm.push("");
2365 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2366 self.sm.pop(); self.sm.pop(); self.sm.push("");
2369 self.sm.push("");
2370 self.emit_op(StackOp::Drop);
2371 self.sm.pop();
2372 }
2373 "extractHashSequence" => {
2374 self.emit_op(StackOp::Push(PushValue::Int(36)));
2376 self.sm.push("");
2377 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2378 self.sm.pop();
2379 self.sm.push("");
2380 self.sm.push("");
2381 self.emit_op(StackOp::Nip);
2382 self.sm.pop();
2383 self.sm.pop();
2384 self.sm.push("");
2385 self.emit_op(StackOp::Push(PushValue::Int(32)));
2386 self.sm.push("");
2387 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2388 self.sm.pop(); self.sm.pop(); self.sm.push("");
2391 self.sm.push("");
2392 self.emit_op(StackOp::Drop);
2393 self.sm.pop();
2394 }
2395 "extractOutpoint" => {
2396 self.emit_op(StackOp::Push(PushValue::Int(68)));
2398 self.sm.push("");
2399 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2400 self.sm.pop();
2401 self.sm.push("");
2402 self.sm.push("");
2403 self.emit_op(StackOp::Nip);
2404 self.sm.pop();
2405 self.sm.pop();
2406 self.sm.push("");
2407 self.emit_op(StackOp::Push(PushValue::Int(36)));
2408 self.sm.push("");
2409 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2410 self.sm.pop(); self.sm.pop(); self.sm.push("");
2413 self.sm.push("");
2414 self.emit_op(StackOp::Drop);
2415 self.sm.pop();
2416 }
2417 "extractSigHashType" => {
2418 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2421 self.sm.push("");
2422 self.sm.push("");
2423 self.emit_op(StackOp::Push(PushValue::Int(4)));
2424 self.sm.push("");
2425 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2426 self.sm.pop();
2427 self.sm.pop();
2428 self.sm.push("");
2429 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2430 self.sm.pop();
2431 self.sm.pop();
2432 self.sm.push("");
2433 self.sm.push("");
2434 self.emit_op(StackOp::Nip);
2435 self.sm.pop();
2436 self.sm.pop();
2437 self.sm.push("");
2438 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2439 }
2440 "extractLocktime" => {
2441 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2444 self.sm.push("");
2445 self.sm.push("");
2446 self.emit_op(StackOp::Push(PushValue::Int(8)));
2447 self.sm.push("");
2448 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2449 self.sm.pop();
2450 self.sm.pop();
2451 self.sm.push("");
2452 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2453 self.sm.pop();
2454 self.sm.pop();
2455 self.sm.push("");
2456 self.sm.push("");
2457 self.emit_op(StackOp::Nip);
2458 self.sm.pop();
2459 self.sm.pop();
2460 self.sm.push("");
2461 self.emit_op(StackOp::Push(PushValue::Int(4)));
2462 self.sm.push("");
2463 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2464 self.sm.pop(); self.sm.pop(); self.sm.push("");
2467 self.sm.push("");
2468 self.emit_op(StackOp::Drop);
2469 self.sm.pop();
2470 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2471 }
2472 "extractOutputHash" | "extractOutputs" => {
2473 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2476 self.sm.push("");
2477 self.sm.push("");
2478 self.emit_op(StackOp::Push(PushValue::Int(40)));
2479 self.sm.push("");
2480 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2481 self.sm.pop();
2482 self.sm.pop();
2483 self.sm.push("");
2484 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2485 self.sm.pop();
2486 self.sm.pop();
2487 self.sm.push("");
2488 self.sm.push("");
2489 self.emit_op(StackOp::Nip);
2490 self.sm.pop();
2491 self.sm.pop();
2492 self.sm.push("");
2493 self.emit_op(StackOp::Push(PushValue::Int(32)));
2494 self.sm.push("");
2495 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2496 self.sm.pop(); self.sm.pop(); self.sm.push("");
2499 self.sm.push("");
2500 self.emit_op(StackOp::Drop);
2501 self.sm.pop();
2502 }
2503 "extractAmount" => {
2504 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2507 self.sm.push("");
2508 self.sm.push("");
2509 self.emit_op(StackOp::Push(PushValue::Int(52)));
2510 self.sm.push("");
2511 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2512 self.sm.pop();
2513 self.sm.pop();
2514 self.sm.push("");
2515 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2516 self.sm.pop();
2517 self.sm.pop();
2518 self.sm.push("");
2519 self.sm.push("");
2520 self.emit_op(StackOp::Nip);
2521 self.sm.pop();
2522 self.sm.pop();
2523 self.sm.push("");
2524 self.emit_op(StackOp::Push(PushValue::Int(8)));
2525 self.sm.push("");
2526 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2527 self.sm.pop(); self.sm.pop(); self.sm.push("");
2530 self.sm.push("");
2531 self.emit_op(StackOp::Drop);
2532 self.sm.pop();
2533 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2534 }
2535 "extractSequence" => {
2536 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2539 self.sm.push("");
2540 self.sm.push("");
2541 self.emit_op(StackOp::Push(PushValue::Int(44)));
2542 self.sm.push("");
2543 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2544 self.sm.pop();
2545 self.sm.pop();
2546 self.sm.push("");
2547 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2548 self.sm.pop();
2549 self.sm.pop();
2550 self.sm.push("");
2551 self.sm.push("");
2552 self.emit_op(StackOp::Nip);
2553 self.sm.pop();
2554 self.sm.pop();
2555 self.sm.push("");
2556 self.emit_op(StackOp::Push(PushValue::Int(4)));
2557 self.sm.push("");
2558 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2559 self.sm.pop(); self.sm.pop(); self.sm.push("");
2562 self.sm.push("");
2563 self.emit_op(StackOp::Drop);
2564 self.sm.pop();
2565 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2566 }
2567 "extractScriptCode" => {
2568 self.emit_op(StackOp::Push(PushValue::Int(104)));
2571 self.sm.push("");
2572 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2573 self.sm.pop();
2574 self.sm.push("");
2575 self.sm.push("");
2576 self.emit_op(StackOp::Nip);
2577 self.sm.pop();
2578 self.sm.pop();
2579 self.sm.push("");
2580 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2581 self.sm.push("");
2582 self.emit_op(StackOp::Push(PushValue::Int(52)));
2583 self.sm.push("");
2584 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2585 self.sm.pop();
2586 self.sm.pop();
2587 self.sm.push("");
2588 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2589 self.sm.pop();
2590 self.sm.pop();
2591 self.sm.push("");
2592 self.sm.push("");
2593 self.emit_op(StackOp::Drop);
2594 self.sm.pop();
2595 }
2596 "extractInputIndex" => {
2597 self.emit_op(StackOp::Push(PushValue::Int(100)));
2600 self.sm.push("");
2601 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2602 self.sm.pop();
2603 self.sm.push("");
2604 self.sm.push("");
2605 self.emit_op(StackOp::Nip);
2606 self.sm.pop();
2607 self.sm.pop();
2608 self.sm.push("");
2609 self.emit_op(StackOp::Push(PushValue::Int(4)));
2610 self.sm.push("");
2611 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2612 self.sm.pop(); self.sm.pop(); self.sm.push("");
2615 self.sm.push("");
2616 self.emit_op(StackOp::Drop);
2617 self.sm.pop();
2618 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2619 }
2620 _ => panic!("unknown extractor: {}", func_name),
2621 }
2622
2623 self.sm.pop();
2625 self.sm.push(binding_name);
2626 self.track_depth();
2627 }
2628
2629 fn lower_array_access(
2643 &mut self,
2644 binding_name: &str,
2645 args: &[String],
2646 binding_index: usize,
2647 last_uses: &HashMap<String, usize>,
2648 ) {
2649 assert!(args.len() >= 2, "__array_access requires 2 arguments (object, index)");
2650
2651 let obj = &args[0];
2652 let index = &args[1];
2653
2654 let obj_is_last = self.is_last_use(obj, binding_index, last_uses);
2656 self.bring_to_top(obj, obj_is_last);
2657
2658 let index_is_last = self.is_last_use(index, binding_index, last_uses);
2660 self.bring_to_top(index, index_is_last);
2661
2662 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2666 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip);
2671 self.sm.pop();
2672 let right_part = self.sm.pop();
2673 self.sm.push(&right_part);
2674
2675 self.emit_op(StackOp::Push(PushValue::Int(1)));
2677 self.sm.push("");
2678
2679 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2683 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
2688 self.sm.pop();
2689 self.sm.pop();
2690 self.sm.push("");
2691
2692 self.sm.pop();
2694 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2695
2696 self.sm.push(binding_name);
2697 self.track_depth();
2698 }
2699
2700 fn lower_reverse_bytes(
2701 &mut self,
2702 binding_name: &str,
2703 args: &[String],
2704 binding_index: usize,
2705 last_uses: &HashMap<String, usize>,
2706 ) {
2707 assert!(!args.is_empty(), "reverseBytes requires 1 argument");
2708 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
2709 self.bring_to_top(&args[0], is_last);
2710
2711 self.sm.pop();
2715
2716 self.emit_op(StackOp::Push(PushValue::Int(0)));
2718 self.emit_op(StackOp::Swap);
2719
2720 for _ in 0..520 {
2722 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2724 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2725 self.emit_op(StackOp::Nip);
2726 self.emit_op(StackOp::If {
2727 then_ops: vec![
2728 StackOp::Push(PushValue::Int(1)),
2729 StackOp::Opcode("OP_SPLIT".to_string()),
2730 StackOp::Swap,
2731 StackOp::Rot,
2732 StackOp::Opcode("OP_CAT".to_string()),
2733 StackOp::Swap,
2734 ],
2735 else_ops: vec![],
2736 });
2737 }
2738
2739 self.emit_op(StackOp::Drop);
2741
2742 self.sm.push(binding_name);
2743 self.track_depth();
2744 }
2745
2746 fn lower_substr(
2747 &mut self,
2748 binding_name: &str,
2749 args: &[String],
2750 binding_index: usize,
2751 last_uses: &HashMap<String, usize>,
2752 ) {
2753 assert!(args.len() >= 3, "substr requires 3 arguments");
2754
2755 let data = &args[0];
2756 let start = &args[1];
2757 let length = &args[2];
2758
2759 let data_is_last = self.is_last_use(data, binding_index, last_uses);
2760 self.bring_to_top(data, data_is_last);
2761
2762 let start_is_last = self.is_last_use(start, binding_index, last_uses);
2763 self.bring_to_top(start, start_is_last);
2764
2765 self.sm.pop();
2766 self.sm.pop();
2767 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2768 self.sm.push("");
2769 self.sm.push("");
2770
2771 self.emit_op(StackOp::Nip);
2772 self.sm.pop();
2773 let right_part = self.sm.pop();
2774 self.sm.push(&right_part);
2775
2776 let len_is_last = self.is_last_use(length, binding_index, last_uses);
2777 self.bring_to_top(length, len_is_last);
2778
2779 self.sm.pop();
2780 self.sm.pop();
2781 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2782 self.sm.push("");
2783 self.sm.push("");
2784
2785 self.emit_op(StackOp::Drop);
2786 self.sm.pop();
2787 self.sm.pop();
2788
2789 self.sm.push(binding_name);
2790 self.track_depth();
2791 }
2792 fn lower_verify_rabin_sig(
2793 &mut self,
2794 binding_name: &str,
2795 args: &[String],
2796 binding_index: usize,
2797 last_uses: &HashMap<String, usize>,
2798 ) {
2799 assert!(args.len() >= 4, "verifyRabinSig requires 4 arguments");
2800
2801 let msg = &args[0];
2805 let sig = &args[1];
2806 let padding = &args[2];
2807 let pub_key = &args[3];
2808
2809 let msg_is_last = self.is_last_use(msg, binding_index, last_uses);
2810 self.bring_to_top(msg, msg_is_last);
2811
2812 let sig_is_last = self.is_last_use(sig, binding_index, last_uses);
2813 self.bring_to_top(sig, sig_is_last);
2814
2815 let padding_is_last = self.is_last_use(padding, binding_index, last_uses);
2816 self.bring_to_top(padding, padding_is_last);
2817
2818 let pub_key_is_last = self.is_last_use(pub_key, binding_index, last_uses);
2819 self.bring_to_top(pub_key, pub_key_is_last);
2820
2821 self.sm.pop();
2823 self.sm.pop();
2824 self.sm.pop();
2825 self.sm.pop();
2826
2827 self.emit_op(StackOp::Opcode("OP_SWAP".to_string())); self.emit_op(StackOp::Opcode("OP_ROT".to_string())); self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2832 self.emit_op(StackOp::Opcode("OP_MUL".to_string())); self.emit_op(StackOp::Opcode("OP_ADD".to_string())); self.emit_op(StackOp::Opcode("OP_SWAP".to_string())); self.emit_op(StackOp::Opcode("OP_MOD".to_string())); self.emit_op(StackOp::Opcode("OP_SWAP".to_string())); self.emit_op(StackOp::Opcode("OP_SHA256".to_string()));
2838 self.emit_op(StackOp::Opcode("OP_EQUAL".to_string()));
2839
2840 self.sm.push(binding_name);
2841 self.track_depth();
2842 }
2843
2844 fn lower_sign(
2847 &mut self,
2848 binding_name: &str,
2849 args: &[String],
2850 binding_index: usize,
2851 last_uses: &HashMap<String, usize>,
2852 ) {
2853 assert!(!args.is_empty(), "sign requires 1 argument");
2854 let x = &args[0];
2855
2856 let x_is_last = self.is_last_use(x, binding_index, last_uses);
2857 self.bring_to_top(x, x_is_last);
2858 self.sm.pop();
2859
2860 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2861 self.emit_op(StackOp::If {
2862 then_ops: vec![
2863 StackOp::Opcode("OP_DUP".to_string()),
2864 StackOp::Opcode("OP_ABS".to_string()),
2865 StackOp::Swap,
2866 StackOp::Opcode("OP_DIV".to_string()),
2867 ],
2868 else_ops: vec![],
2869 });
2870
2871 self.sm.push(binding_name);
2872 self.track_depth();
2873 }
2874
2875 fn lower_right(
2878 &mut self,
2879 binding_name: &str,
2880 args: &[String],
2881 binding_index: usize,
2882 last_uses: &HashMap<String, usize>,
2883 ) {
2884 assert!(args.len() >= 2, "right requires 2 arguments");
2885 let data = &args[0];
2886 let length = &args[1];
2887
2888 let data_is_last = self.is_last_use(data, binding_index, last_uses);
2889 self.bring_to_top(data, data_is_last);
2890
2891 let length_is_last = self.is_last_use(length, binding_index, last_uses);
2892 self.bring_to_top(length, length_is_last);
2893
2894 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Swap); self.emit_op(StackOp::Opcode("OP_SIZE".to_string())); self.emit_op(StackOp::Rot); self.emit_op(StackOp::Opcode("OP_SUB".to_string())); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string())); self.emit_op(StackOp::Nip); self.sm.push(binding_name);
2905 self.track_depth();
2906 }
2907
2908 fn emit_wots_one_chain(&mut self, chain_index: usize) {
2912 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2914 self.emit_op(StackOp::Push(PushValue::Int(15)));
2915 self.emit_op(StackOp::Swap);
2916 self.emit_op(StackOp::Opcode("OP_SUB".into()));
2917 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2921 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2923 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2928 self.emit_op(StackOp::Push(PushValue::Int(32)));
2929 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2930 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2932 for j in 0..15usize {
2938 let adrs_bytes = vec![chain_index as u8, j as u8];
2939 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2940 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".into()));
2941 self.emit_op(StackOp::If {
2942 then_ops: vec![
2943 StackOp::Opcode("OP_1SUB".into()), ],
2945 else_ops: vec![
2946 StackOp::Swap, StackOp::Push(PushValue::Int(2)),
2948 StackOp::Opcode("OP_PICK".into()), StackOp::Push(PushValue::Bytes(adrs_bytes)), StackOp::Opcode("OP_CAT".into()), StackOp::Swap, StackOp::Opcode("OP_CAT".into()), StackOp::Opcode("OP_SHA256".into()), StackOp::Swap, ],
2956 });
2957 }
2958 self.emit_op(StackOp::Drop); self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2963 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2964 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2965 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2966
2967 self.emit_op(StackOp::Rot);
2969 self.emit_op(StackOp::Opcode("OP_ADD".into()));
2970
2971 self.emit_op(StackOp::Swap);
2973 self.emit_op(StackOp::Push(PushValue::Int(3)));
2974 self.emit_op(StackOp::Opcode("OP_ROLL".into()));
2975 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2976 }
2977
2978 fn lower_verify_wots(
2982 &mut self,
2983 binding_name: &str,
2984 args: &[String],
2985 binding_index: usize,
2986 last_uses: &HashMap<String, usize>,
2987 ) {
2988 assert!(args.len() >= 3, "verifyWOTS requires 3 arguments: msg, sig, pubkey");
2989
2990 for arg in args.iter() {
2991 let is_last = self.is_last_use(arg, binding_index, last_uses);
2992 self.bring_to_top(arg, is_last);
2993 }
2994 for _ in 0..3 { self.sm.pop(); }
2995 self.emit_op(StackOp::Push(PushValue::Int(32)));
2999 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Rot); self.emit_op(StackOp::Rot); self.emit_op(StackOp::Swap); self.emit_op(StackOp::Opcode("OP_SHA256".into())); self.emit_op(StackOp::Swap);
3010 self.emit_op(StackOp::Push(PushValue::Int(0)));
3011 self.emit_op(StackOp::Opcode("OP_0".into()));
3012 self.emit_op(StackOp::Push(PushValue::Int(3)));
3013 self.emit_op(StackOp::Opcode("OP_ROLL".into()));
3014
3015 for byte_idx in 0..32 {
3017 if byte_idx < 31 {
3018 self.emit_op(StackOp::Push(PushValue::Int(1)));
3019 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
3020 self.emit_op(StackOp::Swap);
3021 }
3022 self.emit_op(StackOp::Push(PushValue::Int(0)));
3024 self.emit_op(StackOp::Push(PushValue::Int(1)));
3025 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
3026 self.emit_op(StackOp::Opcode("OP_CAT".into()));
3027 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
3028 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3030 self.emit_op(StackOp::Push(PushValue::Int(16)));
3031 self.emit_op(StackOp::Opcode("OP_DIV".into()));
3032 self.emit_op(StackOp::Swap);
3033 self.emit_op(StackOp::Push(PushValue::Int(16)));
3034 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3035
3036 if byte_idx < 31 {
3037 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3038 self.emit_op(StackOp::Swap);
3039 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3040 } else {
3041 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3042 }
3043
3044 self.emit_wots_one_chain(byte_idx * 2); if byte_idx < 31 {
3047 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3048 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3049 self.emit_op(StackOp::Swap);
3050 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3051 } else {
3052 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3053 }
3054
3055 self.emit_wots_one_chain(byte_idx * 2 + 1); if byte_idx < 31 {
3058 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3059 }
3060 }
3061
3062 self.emit_op(StackOp::Swap);
3064 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3066 self.emit_op(StackOp::Push(PushValue::Int(16)));
3067 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3068 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3069 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3071 self.emit_op(StackOp::Push(PushValue::Int(16)));
3072 self.emit_op(StackOp::Opcode("OP_DIV".into()));
3073 self.emit_op(StackOp::Push(PushValue::Int(16)));
3074 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3075 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3076 self.emit_op(StackOp::Push(PushValue::Int(256)));
3078 self.emit_op(StackOp::Opcode("OP_DIV".into()));
3079 self.emit_op(StackOp::Push(PushValue::Int(16)));
3080 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3081 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3082
3083 for ci in 0..3 {
3085 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3086 self.emit_op(StackOp::Push(PushValue::Int(0)));
3087 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3088 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3089 self.emit_wots_one_chain(64 + ci);
3090 self.emit_op(StackOp::Swap);
3091 self.emit_op(StackOp::Drop);
3092 }
3093
3094 self.emit_op(StackOp::Swap);
3096 self.emit_op(StackOp::Drop);
3097 self.emit_op(StackOp::Opcode("OP_SHA256".into()));
3099 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into())); self.emit_op(StackOp::Opcode("OP_EQUAL".into()));
3101 self.emit_op(StackOp::Swap);
3103 self.emit_op(StackOp::Drop);
3104
3105 self.sm.push(binding_name);
3106 self.track_depth();
3107 }
3108
3109 fn lower_verify_slh_dsa(
3113 &mut self,
3114 binding_name: &str,
3115 param_key: &str,
3116 args: &[String],
3117 binding_index: usize,
3118 last_uses: &HashMap<String, usize>,
3119 ) {
3120 assert!(
3121 args.len() >= 3,
3122 "verifySLHDSA requires 3 arguments: msg, sig, pubkey"
3123 );
3124
3125 for arg in args.iter() {
3127 let is_last = self.is_last_use(arg, binding_index, last_uses);
3128 self.bring_to_top(arg, is_last);
3129 }
3130 for _ in 0..3 {
3131 self.sm.pop();
3132 }
3133
3134 super::slh_dsa::emit_verify_slh_dsa(&mut |op| self.ops.push(op), param_key);
3136
3137 self.sm.push(binding_name);
3138 self.track_depth();
3139 }
3140
3141 fn lower_sha256_compress(
3146 &mut self,
3147 binding_name: &str,
3148 args: &[String],
3149 binding_index: usize,
3150 last_uses: &HashMap<String, usize>,
3151 ) {
3152 assert!(
3153 args.len() >= 2,
3154 "sha256Compress requires 2 arguments: state, block"
3155 );
3156 for arg in args.iter() {
3157 let is_last = self.is_last_use(arg, binding_index, last_uses);
3158 self.bring_to_top(arg, is_last);
3159 }
3160 for _ in 0..2 {
3161 self.sm.pop();
3162 }
3163
3164 super::sha256::emit_sha256_compress(&mut |op| self.ops.push(op));
3165
3166 self.sm.push(binding_name);
3167 self.track_depth();
3168 }
3169
3170 fn lower_sha256_finalize(
3171 &mut self,
3172 binding_name: &str,
3173 args: &[String],
3174 binding_index: usize,
3175 last_uses: &HashMap<String, usize>,
3176 ) {
3177 assert!(
3178 args.len() >= 3,
3179 "sha256Finalize requires 3 arguments: state, remaining, msgBitLen"
3180 );
3181 for arg in args.iter() {
3182 let is_last = self.is_last_use(arg, binding_index, last_uses);
3183 self.bring_to_top(arg, is_last);
3184 }
3185 for _ in 0..3 {
3186 self.sm.pop();
3187 }
3188
3189 super::sha256::emit_sha256_finalize(&mut |op| self.ops.push(op));
3190
3191 self.sm.push(binding_name);
3192 self.track_depth();
3193 }
3194
3195 fn lower_blake3_compress(
3196 &mut self,
3197 binding_name: &str,
3198 args: &[String],
3199 binding_index: usize,
3200 last_uses: &HashMap<String, usize>,
3201 ) {
3202 assert!(
3203 args.len() >= 2,
3204 "blake3Compress requires 2 arguments: chainingValue, block"
3205 );
3206 for arg in args.iter() {
3207 let is_last = self.is_last_use(arg, binding_index, last_uses);
3208 self.bring_to_top(arg, is_last);
3209 }
3210 for _ in 0..2 {
3211 self.sm.pop();
3212 }
3213
3214 super::blake3::emit_blake3_compress(&mut |op| self.ops.push(op));
3215
3216 self.sm.push(binding_name);
3217 self.track_depth();
3218 }
3219
3220 fn lower_blake3_hash(
3221 &mut self,
3222 binding_name: &str,
3223 args: &[String],
3224 binding_index: usize,
3225 last_uses: &HashMap<String, usize>,
3226 ) {
3227 assert!(
3228 args.len() >= 1,
3229 "blake3Hash requires 1 argument: message"
3230 );
3231 for arg in args.iter() {
3232 let is_last = self.is_last_use(arg, binding_index, last_uses);
3233 self.bring_to_top(arg, is_last);
3234 }
3235 for _ in 0..1 {
3236 self.sm.pop();
3237 }
3238
3239 super::blake3::emit_blake3_hash(&mut |op| self.ops.push(op));
3240
3241 self.sm.push(binding_name);
3242 self.track_depth();
3243 }
3244
3245 fn lower_ec_builtin(
3246 &mut self,
3247 binding_name: &str,
3248 func_name: &str,
3249 args: &[String],
3250 binding_index: usize,
3251 last_uses: &HashMap<String, usize>,
3252 ) {
3253 for arg in args.iter() {
3255 let is_last = self.is_last_use(arg, binding_index, last_uses);
3256 self.bring_to_top(arg, is_last);
3257 }
3258 for _ in args {
3259 self.sm.pop();
3260 }
3261
3262 let emit = &mut |op: StackOp| self.ops.push(op);
3263
3264 match func_name {
3265 "ecAdd" => super::ec::emit_ec_add(emit),
3266 "ecMul" => super::ec::emit_ec_mul(emit),
3267 "ecMulGen" => super::ec::emit_ec_mul_gen(emit),
3268 "ecNegate" => super::ec::emit_ec_negate(emit),
3269 "ecOnCurve" => super::ec::emit_ec_on_curve(emit),
3270 "ecModReduce" => super::ec::emit_ec_mod_reduce(emit),
3271 "ecEncodeCompressed" => super::ec::emit_ec_encode_compressed(emit),
3272 "ecMakePoint" => super::ec::emit_ec_make_point(emit),
3273 "ecPointX" => super::ec::emit_ec_point_x(emit),
3274 "ecPointY" => super::ec::emit_ec_point_y(emit),
3275 _ => panic!("unknown EC builtin: {}", func_name),
3276 }
3277
3278 self.sm.push(binding_name);
3279 self.track_depth();
3280 }
3281
3282 fn lower_safediv(
3285 &mut self,
3286 binding_name: &str,
3287 args: &[String],
3288 binding_index: usize,
3289 last_uses: &HashMap<String, usize>,
3290 ) {
3291 assert!(args.len() >= 2, "safediv requires 2 arguments");
3292
3293 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3294 self.bring_to_top(&args[0], a_is_last);
3295
3296 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3297 self.bring_to_top(&args[1], b_is_last);
3298
3299 self.sm.pop();
3300 self.sm.pop();
3301
3302 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3303 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string()));
3304 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
3305 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3306
3307 self.sm.push(binding_name);
3308 self.track_depth();
3309 }
3310
3311 fn lower_safemod(
3314 &mut self,
3315 binding_name: &str,
3316 args: &[String],
3317 binding_index: usize,
3318 last_uses: &HashMap<String, usize>,
3319 ) {
3320 assert!(args.len() >= 2, "safemod requires 2 arguments");
3321
3322 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3323 self.bring_to_top(&args[0], a_is_last);
3324
3325 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3326 self.bring_to_top(&args[1], b_is_last);
3327
3328 self.sm.pop();
3329 self.sm.pop();
3330
3331 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3332 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string()));
3333 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
3334 self.emit_op(StackOp::Opcode("OP_MOD".to_string()));
3335
3336 self.sm.push(binding_name);
3337 self.track_depth();
3338 }
3339
3340 fn lower_clamp(
3343 &mut self,
3344 binding_name: &str,
3345 args: &[String],
3346 binding_index: usize,
3347 last_uses: &HashMap<String, usize>,
3348 ) {
3349 assert!(args.len() >= 3, "clamp requires 3 arguments");
3350
3351 let val_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3352 self.bring_to_top(&args[0], val_is_last);
3353
3354 let lo_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3355 self.bring_to_top(&args[1], lo_is_last);
3356
3357 self.sm.pop();
3358 self.sm.pop();
3359 self.emit_op(StackOp::Opcode("OP_MAX".to_string()));
3360 self.sm.push(""); let hi_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3363 self.bring_to_top(&args[2], hi_is_last);
3364
3365 self.sm.pop();
3366 self.sm.pop();
3367 self.emit_op(StackOp::Opcode("OP_MIN".to_string()));
3368
3369 self.sm.push(binding_name);
3370 self.track_depth();
3371 }
3372
3373 fn lower_pow(
3378 &mut self,
3379 binding_name: &str,
3380 args: &[String],
3381 binding_index: usize,
3382 last_uses: &HashMap<String, usize>,
3383 ) {
3384 assert!(args.len() >= 2, "pow requires 2 arguments");
3385
3386 let base_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3387 self.bring_to_top(&args[0], base_is_last);
3388
3389 let exp_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3390 self.bring_to_top(&args[1], exp_is_last);
3391
3392 self.sm.pop();
3393 self.sm.pop();
3394
3395 self.emit_op(StackOp::Swap); self.emit_op(StackOp::Push(PushValue::Int(1))); for i in 0..32 {
3400 self.emit_op(StackOp::Push(PushValue::Int(2)));
3402 self.emit_op(StackOp::Opcode("OP_PICK".to_string())); self.emit_op(StackOp::Push(PushValue::Int(i)));
3404 self.emit_op(StackOp::Opcode("OP_GREATERTHAN".to_string())); self.emit_op(StackOp::If {
3406 then_ops: vec![
3407 StackOp::Over, StackOp::Opcode("OP_MUL".to_string()), ],
3410 else_ops: vec![],
3411 });
3412 }
3413 self.emit_op(StackOp::Nip); self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3418 self.track_depth();
3419 }
3420
3421 fn lower_mul_div(
3424 &mut self,
3425 binding_name: &str,
3426 args: &[String],
3427 binding_index: usize,
3428 last_uses: &HashMap<String, usize>,
3429 ) {
3430 assert!(args.len() >= 3, "mulDiv requires 3 arguments");
3431
3432 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3433 self.bring_to_top(&args[0], a_is_last);
3434
3435 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3436 self.bring_to_top(&args[1], b_is_last);
3437
3438 self.sm.pop();
3439 self.sm.pop();
3440 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3441 self.sm.push(""); let c_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3444 self.bring_to_top(&args[2], c_is_last);
3445
3446 self.sm.pop();
3447 self.sm.pop();
3448 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3449
3450 self.sm.push(binding_name);
3451 self.track_depth();
3452 }
3453
3454 fn lower_percent_of(
3457 &mut self,
3458 binding_name: &str,
3459 args: &[String],
3460 binding_index: usize,
3461 last_uses: &HashMap<String, usize>,
3462 ) {
3463 assert!(args.len() >= 2, "percentOf requires 2 arguments");
3464
3465 let amount_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3466 self.bring_to_top(&args[0], amount_is_last);
3467
3468 let bps_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3469 self.bring_to_top(&args[1], bps_is_last);
3470
3471 self.sm.pop();
3472 self.sm.pop();
3473
3474 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3475 self.emit_op(StackOp::Push(PushValue::Int(10000)));
3476 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3477
3478 self.sm.push(binding_name);
3479 self.track_depth();
3480 }
3481
3482 fn lower_sqrt(
3486 &mut self,
3487 binding_name: &str,
3488 args: &[String],
3489 binding_index: usize,
3490 last_uses: &HashMap<String, usize>,
3491 ) {
3492 assert!(!args.is_empty(), "sqrt requires 1 argument");
3493
3494 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3495 self.bring_to_top(&args[0], n_is_last);
3496
3497 self.sm.pop();
3498
3499 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3502 let mut newton_ops = Vec::new();
3506 newton_ops.push(StackOp::Opcode("OP_DUP".to_string()));
3509 for _ in 0..16 {
3513 newton_ops.push(StackOp::Over); newton_ops.push(StackOp::Over); newton_ops.push(StackOp::Opcode("OP_DIV".to_string())); newton_ops.push(StackOp::Opcode("OP_ADD".to_string())); newton_ops.push(StackOp::Push(PushValue::Int(2))); newton_ops.push(StackOp::Opcode("OP_DIV".to_string())); }
3521
3522 newton_ops.push(StackOp::Opcode("OP_NIP".to_string()));
3525
3526 self.emit_op(StackOp::If {
3527 then_ops: newton_ops,
3528 else_ops: vec![], });
3530
3531 self.sm.push(binding_name);
3532 self.track_depth();
3533 }
3534
3535 fn lower_gcd(
3538 &mut self,
3539 binding_name: &str,
3540 args: &[String],
3541 binding_index: usize,
3542 last_uses: &HashMap<String, usize>,
3543 ) {
3544 assert!(args.len() >= 2, "gcd requires 2 arguments");
3545
3546 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3547 self.bring_to_top(&args[0], a_is_last);
3548
3549 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3550 self.bring_to_top(&args[1], b_is_last);
3551
3552 self.sm.pop();
3553 self.sm.pop();
3554
3555 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3558 self.emit_op(StackOp::Swap);
3559 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3560 self.emit_op(StackOp::Swap);
3561 for _ in 0..256 {
3565 self.emit_op(StackOp::Opcode("OP_DUP".to_string())); self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string())); self.emit_op(StackOp::If {
3571 then_ops: vec![
3572 StackOp::Opcode("OP_TUCK".to_string()), StackOp::Opcode("OP_MOD".to_string()), ],
3577 else_ops: vec![
3578 ],
3580 });
3581 }
3582
3583 self.emit_op(StackOp::Drop);
3586
3587 self.sm.push(binding_name);
3588 self.track_depth();
3589 }
3590
3591 fn lower_divmod(
3594 &mut self,
3595 binding_name: &str,
3596 args: &[String],
3597 binding_index: usize,
3598 last_uses: &HashMap<String, usize>,
3599 ) {
3600 assert!(args.len() >= 2, "divmod requires 2 arguments");
3601
3602 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3603 self.bring_to_top(&args[0], a_is_last);
3604
3605 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3606 self.bring_to_top(&args[1], b_is_last);
3607
3608 self.sm.pop();
3609 self.sm.pop();
3610
3611 self.emit_op(StackOp::Opcode("OP_2DUP".to_string())); self.emit_op(StackOp::Opcode("OP_DIV".to_string())); self.emit_op(StackOp::Opcode("OP_ROT".to_string())); self.emit_op(StackOp::Opcode("OP_ROT".to_string())); self.emit_op(StackOp::Opcode("OP_MOD".to_string())); self.emit_op(StackOp::Drop);
3621
3622 self.sm.push(binding_name);
3623 self.track_depth();
3624 }
3625
3626 fn lower_log2(
3636 &mut self,
3637 binding_name: &str,
3638 args: &[String],
3639 binding_index: usize,
3640 last_uses: &HashMap<String, usize>,
3641 ) {
3642 assert!(!args.is_empty(), "log2 requires 1 argument");
3643
3644 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3645 self.bring_to_top(&args[0], n_is_last);
3646
3647 self.sm.pop();
3648
3649 self.emit_op(StackOp::Push(PushValue::Int(0))); const LOG2_ITERATIONS: usize = 64;
3655 for _ in 0..LOG2_ITERATIONS {
3656 self.emit_op(StackOp::Swap); self.emit_op(StackOp::Opcode("OP_DUP".to_string())); self.emit_op(StackOp::Push(PushValue::Int(1))); self.emit_op(StackOp::Opcode("OP_GREATERTHAN".to_string())); self.emit_op(StackOp::If {
3662 then_ops: vec![
3663 StackOp::Push(PushValue::Int(2)), StackOp::Opcode("OP_DIV".to_string()), StackOp::Swap, StackOp::Opcode("OP_1ADD".to_string()), StackOp::Swap, ],
3669 else_ops: vec![],
3670 });
3671 self.emit_op(StackOp::Swap); }
3675 self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3680 self.track_depth();
3681 }
3682}
3683
3684pub fn lower_to_stack(program: &ANFProgram) -> Result<Vec<StackMethod>, String> {
3692 let mut private_methods: HashMap<String, ANFMethod> = HashMap::new();
3694 for method in &program.methods {
3695 if !method.is_public && method.name != "constructor" {
3696 private_methods.insert(method.name.clone(), method.clone());
3697 }
3698 }
3699
3700 let mut methods = Vec::new();
3701
3702 for method in &program.methods {
3703 if method.name == "constructor" || (!method.is_public && method.name != "constructor") {
3705 continue;
3706 }
3707 let sm = lower_method_with_private_methods(method, &program.properties, &private_methods)?;
3708 methods.push(sm);
3709 }
3710
3711 Ok(methods)
3712}
3713
3714fn method_uses_check_preimage(bindings: &[ANFBinding]) -> bool {
3718 bindings.iter().any(|b| matches!(&b.value, ANFValue::CheckPreimage { .. }))
3719}
3720
3721fn method_uses_code_part(bindings: &[ANFBinding]) -> bool {
3725 bindings.iter().any(|b| match &b.value {
3726 ANFValue::AddOutput { .. } | ANFValue::AddRawOutput { .. } => true,
3727 ANFValue::Call { func, .. } if func == "computeStateOutput" || func == "computeStateOutputHash" => true,
3728 ANFValue::If { then, else_branch, .. } => method_uses_code_part(then) || method_uses_code_part(else_branch),
3729 ANFValue::Loop { body, .. } => method_uses_code_part(body),
3730 _ => false,
3731 })
3732}
3733
3734fn lower_method_with_private_methods(
3735 method: &ANFMethod,
3736 properties: &[ANFProperty],
3737 private_methods: &HashMap<String, ANFMethod>,
3738) -> Result<StackMethod, String> {
3739 let mut param_names: Vec<String> = method.params.iter().map(|p| p.name.clone()).collect();
3740
3741 if method_uses_check_preimage(&method.body) {
3747 param_names.insert(0, "_opPushTxSig".to_string());
3748 if method_uses_code_part(&method.body) {
3752 param_names.insert(0, "_codePart".to_string());
3753 }
3754 }
3755
3756 let mut ctx = LoweringContext::new(¶m_names, properties);
3757 ctx.private_methods = private_methods.clone();
3758 ctx.lower_bindings(&method.body, method.is_public);
3761
3762 let has_deserialize_state = method.body.iter().any(|b| matches!(&b.value, ANFValue::DeserializeState { .. }));
3764 if method.is_public && has_deserialize_state && ctx.sm.depth() > 1 {
3765 let excess = ctx.sm.depth() - 1;
3766 for _ in 0..excess {
3767 ctx.emit_op(StackOp::Nip);
3768 ctx.sm.remove_at_depth(1);
3769 }
3770 }
3771
3772 if ctx.max_depth > MAX_STACK_DEPTH {
3773 return Err(format!(
3774 "method '{}' exceeds maximum stack depth of {} (actual: {}). Simplify the contract logic.",
3775 method.name, MAX_STACK_DEPTH, ctx.max_depth
3776 ));
3777 }
3778
3779 Ok(StackMethod {
3780 name: method.name.clone(),
3781 ops: ctx.ops,
3782 max_stack_depth: ctx.max_depth,
3783 })
3784}
3785
3786fn hex_to_bytes(hex_str: &str) -> Vec<u8> {
3791 if hex_str.is_empty() {
3792 return Vec::new();
3793 }
3794 assert!(
3795 hex_str.len() % 2 == 0,
3796 "invalid hex string length: {}",
3797 hex_str.len()
3798 );
3799 (0..hex_str.len())
3800 .step_by(2)
3801 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap_or(0))
3802 .collect()
3803}
3804
3805#[cfg(test)]
3810mod tests {
3811 use super::*;
3812 use crate::ir::{ANFBinding, ANFMethod, ANFParam, ANFProgram, ANFProperty, ANFValue};
3813
3814 fn p2pkh_program() -> ANFProgram {
3816 ANFProgram {
3817 contract_name: "P2PKH".to_string(),
3818 properties: vec![ANFProperty {
3819 name: "pubKeyHash".to_string(),
3820 prop_type: "Addr".to_string(),
3821 readonly: true,
3822 initial_value: None,
3823 }],
3824 methods: vec![ANFMethod {
3825 name: "unlock".to_string(),
3826 params: vec![
3827 ANFParam {
3828 name: "sig".to_string(),
3829 param_type: "Sig".to_string(),
3830 },
3831 ANFParam {
3832 name: "pubKey".to_string(),
3833 param_type: "PubKey".to_string(),
3834 },
3835 ],
3836 body: vec![
3837 ANFBinding {
3838 name: "t0".to_string(),
3839 value: ANFValue::LoadParam {
3840 name: "sig".to_string(),
3841 },
3842 },
3843 ANFBinding {
3844 name: "t1".to_string(),
3845 value: ANFValue::LoadParam {
3846 name: "pubKey".to_string(),
3847 },
3848 },
3849 ANFBinding {
3850 name: "t2".to_string(),
3851 value: ANFValue::LoadProp {
3852 name: "pubKeyHash".to_string(),
3853 },
3854 },
3855 ANFBinding {
3856 name: "t3".to_string(),
3857 value: ANFValue::Call {
3858 func: "hash160".to_string(),
3859 args: vec!["t1".to_string()],
3860 },
3861 },
3862 ANFBinding {
3863 name: "t4".to_string(),
3864 value: ANFValue::BinOp {
3865 op: "===".to_string(),
3866 left: "t3".to_string(),
3867 right: "t2".to_string(),
3868 result_type: None,
3869 },
3870 },
3871 ANFBinding {
3872 name: "t5".to_string(),
3873 value: ANFValue::Assert {
3874 value: "t4".to_string(),
3875 },
3876 },
3877 ANFBinding {
3878 name: "t6".to_string(),
3879 value: ANFValue::Call {
3880 func: "checkSig".to_string(),
3881 args: vec!["t0".to_string(), "t1".to_string()],
3882 },
3883 },
3884 ANFBinding {
3885 name: "t7".to_string(),
3886 value: ANFValue::Assert {
3887 value: "t6".to_string(),
3888 },
3889 },
3890 ],
3891 is_public: true,
3892 }],
3893 }
3894 }
3895
3896 #[test]
3897 fn test_p2pkh_stack_lowering_produces_placeholder_ops() {
3898 let program = p2pkh_program();
3899 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3900 assert_eq!(methods.len(), 1);
3901 assert_eq!(methods[0].name, "unlock");
3902
3903 let has_placeholder = methods[0].ops.iter().any(|op| {
3905 matches!(op, StackOp::Placeholder { .. })
3906 });
3907 assert!(
3908 has_placeholder,
3909 "P2PKH should have Placeholder ops for constructor params, ops: {:?}",
3910 methods[0].ops
3911 );
3912 }
3913
3914 #[test]
3915 fn test_placeholder_has_correct_param_index() {
3916 let program = p2pkh_program();
3917 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3918
3919 let placeholders: Vec<&StackOp> = methods[0]
3921 .ops
3922 .iter()
3923 .filter(|op| matches!(op, StackOp::Placeholder { .. }))
3924 .collect();
3925
3926 assert!(
3927 !placeholders.is_empty(),
3928 "should have at least one Placeholder"
3929 );
3930
3931 if let StackOp::Placeholder {
3933 param_index,
3934 param_name,
3935 } = placeholders[0]
3936 {
3937 assert_eq!(*param_index, 0);
3938 assert_eq!(param_name, "pubKeyHash");
3939 } else {
3940 panic!("expected Placeholder op");
3941 }
3942 }
3943
3944 #[test]
3945 fn test_with_initial_values_no_placeholder_ops() {
3946 let mut program = p2pkh_program();
3947 program.properties[0].initial_value =
3949 Some(serde_json::Value::String("aabbccdd".to_string()));
3950
3951 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3952 let has_placeholder = methods[0].ops.iter().any(|op| {
3953 matches!(op, StackOp::Placeholder { .. })
3954 });
3955 assert!(
3956 !has_placeholder,
3957 "with initial values, there should be no Placeholder ops"
3958 );
3959 }
3960
3961 #[test]
3962 fn test_stack_lowering_produces_standard_opcodes() {
3963 let program = p2pkh_program();
3964 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3965
3966 let opcodes: Vec<&str> = methods[0]
3968 .ops
3969 .iter()
3970 .filter_map(|op| match op {
3971 StackOp::Opcode(code) => Some(code.as_str()),
3972 _ => None,
3973 })
3974 .collect();
3975
3976 assert!(
3978 opcodes.contains(&"OP_HASH160"),
3979 "expected OP_HASH160 in opcodes: {:?}",
3980 opcodes
3981 );
3982 assert!(
3983 opcodes.contains(&"OP_CHECKSIG"),
3984 "expected OP_CHECKSIG in opcodes: {:?}",
3985 opcodes
3986 );
3987 }
3988
3989 #[test]
3990 fn test_max_stack_depth_is_tracked() {
3991 let program = p2pkh_program();
3992 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3993 assert!(
3994 methods[0].max_stack_depth > 0,
3995 "max_stack_depth should be > 0"
3996 );
3997 assert!(
3999 methods[0].max_stack_depth <= 10,
4000 "max_stack_depth should be reasonable for P2PKH, got: {}",
4001 methods[0].max_stack_depth
4002 );
4003 }
4004
4005 fn collect_all_opcodes(ops: &[StackOp]) -> Vec<String> {
4010 let mut result = Vec::new();
4011 for op in ops {
4012 match op {
4013 StackOp::Opcode(code) => result.push(code.clone()),
4014 StackOp::If { then_ops, else_ops } => {
4015 result.push("OP_IF".to_string());
4016 result.extend(collect_all_opcodes(then_ops));
4017 result.push("OP_ELSE".to_string());
4018 result.extend(collect_all_opcodes(else_ops));
4019 result.push("OP_ENDIF".to_string());
4020 }
4021 StackOp::Push(PushValue::Int(n)) => {
4022 result.push(format!("PUSH({})", n));
4023 }
4024 StackOp::Drop => result.push("OP_DROP".to_string()),
4025 StackOp::Swap => result.push("OP_SWAP".to_string()),
4026 StackOp::Dup => result.push("OP_DUP".to_string()),
4027 StackOp::Over => result.push("OP_OVER".to_string()),
4028 StackOp::Rot => result.push("OP_ROT".to_string()),
4029 StackOp::Nip => result.push("OP_NIP".to_string()),
4030 _ => {}
4031 }
4032 }
4033 result
4034 }
4035
4036 fn collect_opcodes_in_if_branches(ops: &[StackOp]) -> (Vec<String>, Vec<String>) {
4037 for op in ops {
4038 if let StackOp::If { then_ops, else_ops } = op {
4039 return (collect_all_opcodes(then_ops), collect_all_opcodes(else_ops));
4040 }
4041 }
4042 (vec![], vec![])
4043 }
4044
4045 #[test]
4050 fn test_extract_output_hash_uses_offset_40() {
4051 let program = ANFProgram {
4053 contract_name: "TestExtract".to_string(),
4054 properties: vec![ANFProperty {
4055 name: "val".to_string(),
4056 prop_type: "bigint".to_string(),
4057 readonly: false,
4058 initial_value: Some(serde_json::Value::Number(serde_json::Number::from(0))),
4059 }],
4060 methods: vec![ANFMethod {
4061 name: "check".to_string(),
4062 params: vec![
4063 ANFParam { name: "preimage".to_string(), param_type: "SigHashPreimage".to_string() },
4064 ],
4065 body: vec![
4066 ANFBinding {
4067 name: "t0".to_string(),
4068 value: ANFValue::LoadParam { name: "preimage".to_string() },
4069 },
4070 ANFBinding {
4071 name: "t1".to_string(),
4072 value: ANFValue::Call {
4073 func: "extractOutputHash".to_string(),
4074 args: vec!["t0".to_string()],
4075 },
4076 },
4077 ANFBinding {
4078 name: "t2".to_string(),
4079 value: ANFValue::LoadConst { value: serde_json::Value::Bool(true) },
4080 },
4081 ANFBinding {
4082 name: "t3".to_string(),
4083 value: ANFValue::Assert { value: "t2".to_string() },
4084 },
4085 ],
4086 is_public: true,
4087 }],
4088 };
4089
4090 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4091 let opcodes = collect_all_opcodes(&methods[0].ops);
4092
4093 assert!(
4095 opcodes.contains(&"PUSH(40)".to_string()),
4096 "extractOutputHash should use offset 40 (BIP-143 hashOutputs starts at size-40), ops: {:?}",
4097 opcodes
4098 );
4099 assert!(
4100 !opcodes.contains(&"PUSH(44)".to_string()),
4101 "extractOutputHash should NOT use offset 44, ops: {:?}",
4102 opcodes
4103 );
4104 }
4105
4106 #[test]
4111 fn test_terminal_if_propagates_terminal_assert() {
4112 let program = ANFProgram {
4115 contract_name: "TerminalIf".to_string(),
4116 properties: vec![],
4117 methods: vec![ANFMethod {
4118 name: "check".to_string(),
4119 params: vec![
4120 ANFParam { name: "mode".to_string(), param_type: "boolean".to_string() },
4121 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4122 ],
4123 body: vec![
4124 ANFBinding {
4125 name: "t0".to_string(),
4126 value: ANFValue::LoadParam { name: "mode".to_string() },
4127 },
4128 ANFBinding {
4129 name: "t1".to_string(),
4130 value: ANFValue::LoadParam { name: "x".to_string() },
4131 },
4132 ANFBinding {
4133 name: "t2".to_string(),
4134 value: ANFValue::If {
4135 cond: "t0".to_string(),
4136 then: vec![
4137 ANFBinding {
4138 name: "t3".to_string(),
4139 value: ANFValue::LoadConst {
4140 value: serde_json::Value::Number(serde_json::Number::from(10)),
4141 },
4142 },
4143 ANFBinding {
4144 name: "t4".to_string(),
4145 value: ANFValue::BinOp {
4146 op: ">".to_string(),
4147 left: "t1".to_string(),
4148 right: "t3".to_string(),
4149 result_type: None,
4150 },
4151 },
4152 ANFBinding {
4153 name: "t5".to_string(),
4154 value: ANFValue::Assert { value: "t4".to_string() },
4155 },
4156 ],
4157 else_branch: vec![
4158 ANFBinding {
4159 name: "t6".to_string(),
4160 value: ANFValue::LoadConst {
4161 value: serde_json::Value::Number(serde_json::Number::from(5)),
4162 },
4163 },
4164 ANFBinding {
4165 name: "t7".to_string(),
4166 value: ANFValue::BinOp {
4167 op: ">".to_string(),
4168 left: "t1".to_string(),
4169 right: "t6".to_string(),
4170 result_type: None,
4171 },
4172 },
4173 ANFBinding {
4174 name: "t8".to_string(),
4175 value: ANFValue::Assert { value: "t7".to_string() },
4176 },
4177 ],
4178 },
4179 },
4180 ],
4181 is_public: true,
4182 }],
4183 };
4184
4185 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4186
4187 let (then_opcodes, else_opcodes) = collect_opcodes_in_if_branches(&methods[0].ops);
4189
4190 assert!(
4192 !then_opcodes.contains(&"OP_VERIFY".to_string()),
4193 "then branch should not contain OP_VERIFY (terminal assert), got: {:?}",
4194 then_opcodes
4195 );
4196 assert!(
4197 !else_opcodes.contains(&"OP_VERIFY".to_string()),
4198 "else branch should not contain OP_VERIFY (terminal assert), got: {:?}",
4199 else_opcodes
4200 );
4201 }
4202
4203 #[test]
4208 fn test_unpack_emits_bin2num() {
4209 let program = ANFProgram {
4210 contract_name: "TestUnpack".to_string(),
4211 properties: vec![],
4212 methods: vec![ANFMethod {
4213 name: "check".to_string(),
4214 params: vec![
4215 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
4216 ],
4217 body: vec![
4218 ANFBinding {
4219 name: "t0".to_string(),
4220 value: ANFValue::LoadParam { name: "data".to_string() },
4221 },
4222 ANFBinding {
4223 name: "t1".to_string(),
4224 value: ANFValue::Call {
4225 func: "unpack".to_string(),
4226 args: vec!["t0".to_string()],
4227 },
4228 },
4229 ANFBinding {
4230 name: "t2".to_string(),
4231 value: ANFValue::LoadConst {
4232 value: serde_json::Value::Number(serde_json::Number::from(42)),
4233 },
4234 },
4235 ANFBinding {
4236 name: "t3".to_string(),
4237 value: ANFValue::BinOp {
4238 op: "===".to_string(),
4239 left: "t1".to_string(),
4240 right: "t2".to_string(),
4241 result_type: None,
4242 },
4243 },
4244 ANFBinding {
4245 name: "t4".to_string(),
4246 value: ANFValue::Assert { value: "t3".to_string() },
4247 },
4248 ],
4249 is_public: true,
4250 }],
4251 };
4252
4253 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4254 let opcodes = collect_all_opcodes(&methods[0].ops);
4255 assert!(
4256 opcodes.contains(&"OP_BIN2NUM".to_string()),
4257 "unpack should emit OP_BIN2NUM, got: {:?}",
4258 opcodes
4259 );
4260 }
4261
4262 #[test]
4263 fn test_pack_is_noop() {
4264 let program = ANFProgram {
4265 contract_name: "TestPack".to_string(),
4266 properties: vec![],
4267 methods: vec![ANFMethod {
4268 name: "check".to_string(),
4269 params: vec![
4270 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4271 ],
4272 body: vec![
4273 ANFBinding {
4274 name: "t0".to_string(),
4275 value: ANFValue::LoadParam { name: "x".to_string() },
4276 },
4277 ANFBinding {
4278 name: "t1".to_string(),
4279 value: ANFValue::Call {
4280 func: "pack".to_string(),
4281 args: vec!["t0".to_string()],
4282 },
4283 },
4284 ANFBinding {
4285 name: "t2".to_string(),
4286 value: ANFValue::LoadConst {
4287 value: serde_json::Value::Bool(true),
4288 },
4289 },
4290 ANFBinding {
4291 name: "t3".to_string(),
4292 value: ANFValue::Assert { value: "t2".to_string() },
4293 },
4294 ],
4295 is_public: true,
4296 }],
4297 };
4298
4299 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4300 let opcodes = collect_all_opcodes(&methods[0].ops);
4301 assert!(
4303 !opcodes.contains(&"OP_BIN2NUM".to_string()),
4304 "pack should not emit OP_BIN2NUM, got: {:?}",
4305 opcodes
4306 );
4307 assert!(
4308 !opcodes.contains(&"OP_NUM2BIN".to_string()),
4309 "pack should not emit OP_NUM2BIN, got: {:?}",
4310 opcodes
4311 );
4312 }
4313
4314 #[test]
4315 fn test_to_byte_string_is_noop() {
4316 let program = ANFProgram {
4317 contract_name: "TestToByteString".to_string(),
4318 properties: vec![],
4319 methods: vec![ANFMethod {
4320 name: "check".to_string(),
4321 params: vec![
4322 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4323 ],
4324 body: vec![
4325 ANFBinding {
4326 name: "t0".to_string(),
4327 value: ANFValue::LoadParam { name: "x".to_string() },
4328 },
4329 ANFBinding {
4330 name: "t1".to_string(),
4331 value: ANFValue::Call {
4332 func: "toByteString".to_string(),
4333 args: vec!["t0".to_string()],
4334 },
4335 },
4336 ANFBinding {
4337 name: "t2".to_string(),
4338 value: ANFValue::LoadConst {
4339 value: serde_json::Value::Bool(true),
4340 },
4341 },
4342 ANFBinding {
4343 name: "t3".to_string(),
4344 value: ANFValue::Assert { value: "t2".to_string() },
4345 },
4346 ],
4347 is_public: true,
4348 }],
4349 };
4350
4351 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4352 let opcodes = collect_all_opcodes(&methods[0].ops);
4353 assert!(
4355 !opcodes.contains(&"OP_BIN2NUM".to_string()),
4356 "toByteString should not emit OP_BIN2NUM, got: {:?}",
4357 opcodes
4358 );
4359 }
4360
4361 #[test]
4366 fn test_sqrt_has_zero_guard() {
4367 let program = ANFProgram {
4368 contract_name: "TestSqrt".to_string(),
4369 properties: vec![],
4370 methods: vec![ANFMethod {
4371 name: "check".to_string(),
4372 params: vec![
4373 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4374 ],
4375 body: vec![
4376 ANFBinding {
4377 name: "t0".to_string(),
4378 value: ANFValue::LoadParam { name: "n".to_string() },
4379 },
4380 ANFBinding {
4381 name: "t1".to_string(),
4382 value: ANFValue::Call {
4383 func: "sqrt".to_string(),
4384 args: vec!["t0".to_string()],
4385 },
4386 },
4387 ANFBinding {
4388 name: "t2".to_string(),
4389 value: ANFValue::LoadConst {
4390 value: serde_json::Value::Number(serde_json::Number::from(0)),
4391 },
4392 },
4393 ANFBinding {
4394 name: "t3".to_string(),
4395 value: ANFValue::BinOp {
4396 op: ">=".to_string(),
4397 left: "t1".to_string(),
4398 right: "t2".to_string(),
4399 result_type: None,
4400 },
4401 },
4402 ANFBinding {
4403 name: "t4".to_string(),
4404 value: ANFValue::Assert { value: "t3".to_string() },
4405 },
4406 ],
4407 is_public: true,
4408 }],
4409 };
4410
4411 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4412 let opcodes = collect_all_opcodes(&methods[0].ops);
4413
4414 let dup_idx = opcodes.iter().position(|o| o == "OP_DUP");
4417 let if_idx = opcodes.iter().position(|o| o == "OP_IF");
4418
4419 assert!(
4420 dup_idx.is_some() && if_idx.is_some(),
4421 "sqrt should have OP_DUP and OP_IF for zero guard, got: {:?}",
4422 opcodes
4423 );
4424 assert!(
4425 dup_idx.unwrap() < if_idx.unwrap(),
4426 "OP_DUP should come before OP_IF in sqrt zero guard, got: {:?}",
4427 opcodes
4428 );
4429 }
4430
4431 #[test]
4436 fn test_loop_cleans_up_unused_iter_var() {
4437 let program = ANFProgram {
4441 contract_name: "TestLoopCleanup".to_string(),
4442 properties: vec![],
4443 methods: vec![ANFMethod {
4444 name: "check".to_string(),
4445 params: vec![
4446 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4447 ],
4448 body: vec![
4449 ANFBinding {
4450 name: "t0".to_string(),
4451 value: ANFValue::LoadParam { name: "x".to_string() },
4452 },
4453 ANFBinding {
4454 name: "t_loop".to_string(),
4455 value: ANFValue::Loop {
4456 count: 3,
4457 body: vec![
4458 ANFBinding {
4460 name: "t1".to_string(),
4461 value: ANFValue::LoadParam { name: "x".to_string() },
4462 },
4463 ANFBinding {
4464 name: "t2".to_string(),
4465 value: ANFValue::Assert { value: "t1".to_string() },
4466 },
4467 ],
4468 iter_var: "__i".to_string(),
4469 },
4470 },
4471 ANFBinding {
4472 name: "t_final".to_string(),
4473 value: ANFValue::LoadConst {
4474 value: serde_json::Value::Bool(true),
4475 },
4476 },
4477 ANFBinding {
4478 name: "t_assert".to_string(),
4479 value: ANFValue::Assert { value: "t_final".to_string() },
4480 },
4481 ],
4482 is_public: true,
4483 }],
4484 };
4485
4486 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4487 let opcodes = collect_all_opcodes(&methods[0].ops);
4488
4489 let drop_count = opcodes.iter().filter(|o| o.as_str() == "OP_DROP").count();
4493 assert!(
4494 drop_count >= 3,
4495 "unused iter var should be dropped after each iteration; expected >= 3 OP_DROPs, got {}: {:?}",
4496 drop_count,
4497 opcodes
4498 );
4499 }
4500
4501 #[test]
4506 fn test_push_value_int_large_values() {
4507 let large_val: i128 = (i64::MAX as i128) + 1;
4509 let push = PushValue::Int(large_val);
4510 if let PushValue::Int(v) = push {
4511 assert_eq!(v, large_val, "PushValue::Int should store values > i64::MAX without truncation");
4512 } else {
4513 panic!("expected PushValue::Int");
4514 }
4515
4516 let neg_val: i128 = (i64::MIN as i128) - 1;
4518 let push_neg = PushValue::Int(neg_val);
4519 if let PushValue::Int(v) = push_neg {
4520 assert_eq!(v, neg_val, "PushValue::Int should store values < i64::MIN without truncation");
4521 } else {
4522 panic!("expected PushValue::Int");
4523 }
4524 }
4525
4526 #[test]
4527 fn test_push_value_int_encodes_large_number() {
4528 use crate::codegen::emit::encode_push_int;
4530
4531 let large_val: i128 = 1i128 << 100;
4532 let (hex, _asm) = encode_push_int(large_val);
4533 assert!(!hex.is_empty(), "encoding of 2^100 should produce non-empty hex");
4535
4536 assert!(
4540 hex.len() >= 26,
4541 "2^100 should need at least 13 bytes of push data, got hex length {}: {}",
4542 hex.len(),
4543 hex
4544 );
4545 }
4546
4547 #[test]
4552 fn test_log2_uses_bit_scanning_not_byte_approx() {
4553 let program = ANFProgram {
4554 contract_name: "TestLog2".to_string(),
4555 properties: vec![],
4556 methods: vec![ANFMethod {
4557 name: "check".to_string(),
4558 params: vec![
4559 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4560 ],
4561 body: vec![
4562 ANFBinding {
4563 name: "t0".to_string(),
4564 value: ANFValue::LoadParam { name: "n".to_string() },
4565 },
4566 ANFBinding {
4567 name: "t1".to_string(),
4568 value: ANFValue::Call {
4569 func: "log2".to_string(),
4570 args: vec!["t0".to_string()],
4571 },
4572 },
4573 ANFBinding {
4574 name: "t2".to_string(),
4575 value: ANFValue::LoadConst {
4576 value: serde_json::Value::Number(serde_json::Number::from(0)),
4577 },
4578 },
4579 ANFBinding {
4580 name: "t3".to_string(),
4581 value: ANFValue::BinOp {
4582 op: ">=".to_string(),
4583 left: "t1".to_string(),
4584 right: "t2".to_string(),
4585 result_type: None,
4586 },
4587 },
4588 ANFBinding {
4589 name: "t4".to_string(),
4590 value: ANFValue::Assert { value: "t3".to_string() },
4591 },
4592 ],
4593 is_public: true,
4594 }],
4595 };
4596
4597 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4598 let opcodes = collect_all_opcodes(&methods[0].ops);
4599
4600 assert!(
4602 opcodes.contains(&"OP_DIV".to_string()),
4603 "log2 should use OP_DIV (bit-scanning), got: {:?}",
4604 opcodes
4605 );
4606 assert!(
4607 opcodes.contains(&"OP_GREATERTHAN".to_string()),
4608 "log2 should use OP_GREATERTHAN (bit-scanning), got: {:?}",
4609 opcodes
4610 );
4611
4612 assert!(
4614 !opcodes.contains(&"OP_SIZE".to_string()),
4615 "log2 should NOT use OP_SIZE (old byte approximation), got: {:?}",
4616 opcodes
4617 );
4618 assert!(
4619 !opcodes.contains(&"OP_MUL".to_string()),
4620 "log2 should NOT use OP_MUL (old byte approximation), got: {:?}",
4621 opcodes
4622 );
4623
4624 assert!(
4626 opcodes.contains(&"OP_1ADD".to_string()),
4627 "log2 should use OP_1ADD (counter increment), got: {:?}",
4628 opcodes
4629 );
4630 }
4631
4632 #[test]
4637 fn test_reverse_bytes_uses_split_cat_not_op_reverse() {
4638 let program = ANFProgram {
4639 contract_name: "TestReverse".to_string(),
4640 properties: vec![],
4641 methods: vec![ANFMethod {
4642 name: "check".to_string(),
4643 params: vec![
4644 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
4645 ],
4646 body: vec![
4647 ANFBinding {
4648 name: "t0".to_string(),
4649 value: ANFValue::LoadParam { name: "data".to_string() },
4650 },
4651 ANFBinding {
4652 name: "t1".to_string(),
4653 value: ANFValue::Call {
4654 func: "reverseBytes".to_string(),
4655 args: vec!["t0".to_string()],
4656 },
4657 },
4658 ANFBinding {
4659 name: "t2".to_string(),
4660 value: ANFValue::LoadConst {
4661 value: serde_json::Value::Bool(true),
4662 },
4663 },
4664 ANFBinding {
4665 name: "t3".to_string(),
4666 value: ANFValue::Assert { value: "t2".to_string() },
4667 },
4668 ],
4669 is_public: true,
4670 }],
4671 };
4672
4673 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4674 let opcodes = collect_all_opcodes(&methods[0].ops);
4675
4676 assert!(
4678 !opcodes.contains(&"OP_REVERSE".to_string()),
4679 "reverseBytes must NOT emit OP_REVERSE (does not exist), got: {:?}",
4680 opcodes
4681 );
4682
4683 assert!(
4685 opcodes.contains(&"OP_SPLIT".to_string()),
4686 "reverseBytes should emit OP_SPLIT for byte peeling, got: {:?}",
4687 opcodes
4688 );
4689 assert!(
4690 opcodes.contains(&"OP_CAT".to_string()),
4691 "reverseBytes should emit OP_CAT for reassembly, got: {:?}",
4692 opcodes
4693 );
4694
4695 assert!(
4697 opcodes.contains(&"OP_SIZE".to_string()),
4698 "reverseBytes should emit OP_SIZE for length check, got: {:?}",
4699 opcodes
4700 );
4701 }
4702
4703 #[test]
4708 fn test_method_count_matches_public_methods() {
4709 let program = p2pkh_program();
4711 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4712
4713 assert_eq!(
4715 methods.len(),
4716 1,
4717 "expected 1 stack method (unlock), got {}: {:?}",
4718 methods.len(),
4719 methods.iter().map(|m| &m.name).collect::<Vec<_>>()
4720 );
4721 assert_eq!(methods[0].name, "unlock");
4722 }
4723
4724 #[test]
4729 fn test_multi_method_dispatch() {
4730 let program = ANFProgram {
4731 contract_name: "Multi".to_string(),
4732 properties: vec![],
4733 methods: vec![
4734 ANFMethod {
4735 name: "constructor".to_string(),
4736 params: vec![],
4737 body: vec![],
4738 is_public: false,
4739 },
4740 ANFMethod {
4741 name: "method1".to_string(),
4742 params: vec![ANFParam {
4743 name: "x".to_string(),
4744 param_type: "bigint".to_string(),
4745 }],
4746 body: vec![
4747 ANFBinding {
4748 name: "t0".to_string(),
4749 value: ANFValue::LoadParam { name: "x".to_string() },
4750 },
4751 ANFBinding {
4752 name: "t1".to_string(),
4753 value: ANFValue::LoadConst {
4754 value: serde_json::Value::Number(serde_json::Number::from(42)),
4755 },
4756 },
4757 ANFBinding {
4758 name: "t2".to_string(),
4759 value: ANFValue::BinOp {
4760 op: "===".to_string(),
4761 left: "t0".to_string(),
4762 right: "t1".to_string(),
4763 result_type: None,
4764 },
4765 },
4766 ANFBinding {
4767 name: "t3".to_string(),
4768 value: ANFValue::Assert { value: "t2".to_string() },
4769 },
4770 ],
4771 is_public: true,
4772 },
4773 ANFMethod {
4774 name: "method2".to_string(),
4775 params: vec![ANFParam {
4776 name: "y".to_string(),
4777 param_type: "bigint".to_string(),
4778 }],
4779 body: vec![
4780 ANFBinding {
4781 name: "t0".to_string(),
4782 value: ANFValue::LoadParam { name: "y".to_string() },
4783 },
4784 ANFBinding {
4785 name: "t1".to_string(),
4786 value: ANFValue::LoadConst {
4787 value: serde_json::Value::Number(serde_json::Number::from(100)),
4788 },
4789 },
4790 ANFBinding {
4791 name: "t2".to_string(),
4792 value: ANFValue::BinOp {
4793 op: "===".to_string(),
4794 left: "t0".to_string(),
4795 right: "t1".to_string(),
4796 result_type: None,
4797 },
4798 },
4799 ANFBinding {
4800 name: "t3".to_string(),
4801 value: ANFValue::Assert { value: "t2".to_string() },
4802 },
4803 ],
4804 is_public: true,
4805 },
4806 ],
4807 };
4808
4809 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4810 assert_eq!(
4811 methods.len(),
4812 2,
4813 "expected 2 stack methods, got {}: {:?}",
4814 methods.len(),
4815 methods.iter().map(|m| &m.name).collect::<Vec<_>>()
4816 );
4817 }
4818
4819 #[test]
4824 fn test_extract_outputs_uses_offset_40() {
4825 let program = ANFProgram {
4826 contract_name: "OutputsCheck".to_string(),
4827 properties: vec![],
4828 methods: vec![ANFMethod {
4829 name: "check".to_string(),
4830 params: vec![ANFParam {
4831 name: "preimage".to_string(),
4832 param_type: "SigHashPreimage".to_string(),
4833 }],
4834 body: vec![
4835 ANFBinding {
4836 name: "t0".to_string(),
4837 value: ANFValue::LoadParam { name: "preimage".to_string() },
4838 },
4839 ANFBinding {
4840 name: "t1".to_string(),
4841 value: ANFValue::Call {
4842 func: "extractOutputs".to_string(),
4843 args: vec!["t0".to_string()],
4844 },
4845 },
4846 ANFBinding {
4847 name: "t2".to_string(),
4848 value: ANFValue::Assert { value: "t1".to_string() },
4849 },
4850 ],
4851 is_public: true,
4852 }],
4853 };
4854
4855 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4856 let opcodes = collect_all_opcodes(&methods[0].ops);
4857
4858 assert!(
4861 opcodes.contains(&"PUSH(40)".to_string()),
4862 "expected PUSH(40) for extractOutputs offset, got: {:?}",
4863 opcodes
4864 );
4865 assert!(
4867 !opcodes.contains(&"PUSH(44)".to_string()),
4868 "extractOutputs should NOT use offset 44, got: {:?}",
4869 opcodes
4870 );
4871 }
4872
4873 #[test]
4879 fn test_arithmetic_ops_contains_add() {
4880 let program = ANFProgram {
4882 contract_name: "ArithCheck".to_string(),
4883 properties: vec![ANFProperty {
4884 name: "target".to_string(),
4885 prop_type: "bigint".to_string(),
4886 readonly: true,
4887 initial_value: None,
4888 }],
4889 methods: vec![ANFMethod {
4890 name: "verify".to_string(),
4891 params: vec![
4892 ANFParam { name: "a".to_string(), param_type: "bigint".to_string() },
4893 ANFParam { name: "b".to_string(), param_type: "bigint".to_string() },
4894 ],
4895 body: vec![
4896 ANFBinding {
4897 name: "t0".to_string(),
4898 value: ANFValue::LoadParam { name: "a".to_string() },
4899 },
4900 ANFBinding {
4901 name: "t1".to_string(),
4902 value: ANFValue::LoadParam { name: "b".to_string() },
4903 },
4904 ANFBinding {
4905 name: "t2".to_string(),
4906 value: ANFValue::BinOp {
4907 op: "+".to_string(),
4908 left: "t0".to_string(),
4909 right: "t1".to_string(),
4910 result_type: None,
4911 },
4912 },
4913 ANFBinding {
4914 name: "t3".to_string(),
4915 value: ANFValue::LoadProp { name: "target".to_string() },
4916 },
4917 ANFBinding {
4918 name: "t4".to_string(),
4919 value: ANFValue::BinOp {
4920 op: "===".to_string(),
4921 left: "t2".to_string(),
4922 right: "t3".to_string(),
4923 result_type: None,
4924 },
4925 },
4926 ANFBinding {
4927 name: "t5".to_string(),
4928 value: ANFValue::Assert { value: "t4".to_string() },
4929 },
4930 ],
4931 is_public: true,
4932 }],
4933 };
4934
4935 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4936 let opcodes = collect_all_opcodes(&methods[0].ops);
4937
4938 assert!(
4940 opcodes.contains(&"OP_ADD".to_string()),
4941 "expected OP_ADD in stack ops for 'a + b', got: {:?}",
4942 opcodes
4943 );
4944
4945 assert!(
4947 opcodes.contains(&"OP_NUMEQUAL".to_string()),
4948 "expected OP_NUMEQUAL in stack ops for '===', got: {:?}",
4949 opcodes
4950 );
4951 }
4952
4953 #[test]
4959 fn test_s18_pick_roll_depth_within_max_stack_depth() {
4960 let program = p2pkh_program();
4961 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4962
4963 let max_depth = methods[0].max_stack_depth;
4964
4965 fn check_ops(ops: &[StackOp], max_depth: usize) {
4966 for op in ops {
4967 match op {
4968 StackOp::Pick { depth } => {
4969 assert!(
4970 *depth < max_depth,
4971 "Pick depth {} must be < max_stack_depth {}",
4972 depth,
4973 max_depth
4974 );
4975 }
4976 StackOp::Roll { depth } => {
4977 assert!(
4978 *depth < max_depth,
4979 "Roll depth {} must be < max_stack_depth {}",
4980 depth,
4981 max_depth
4982 );
4983 }
4984 StackOp::If { then_ops, else_ops } => {
4985 check_ops(then_ops, max_depth);
4986 check_ops(else_ops, max_depth);
4987 }
4988 _ => {}
4989 }
4990 }
4991 }
4992
4993 check_ops(&methods[0].ops, max_depth);
4994 }
4995
4996 #[test]
5002 fn test_bytestring_concat_emits_op_cat() {
5003 let program = ANFProgram {
5004 contract_name: "CatCheck".to_string(),
5005 properties: vec![],
5006 methods: vec![ANFMethod {
5007 name: "verify".to_string(),
5008 params: vec![
5009 ANFParam { name: "a".to_string(), param_type: "ByteString".to_string() },
5010 ANFParam { name: "b".to_string(), param_type: "ByteString".to_string() },
5011 ANFParam { name: "expected".to_string(), param_type: "ByteString".to_string() },
5012 ],
5013 body: vec![
5014 ANFBinding {
5015 name: "t0".to_string(),
5016 value: ANFValue::LoadParam { name: "a".to_string() },
5017 },
5018 ANFBinding {
5019 name: "t1".to_string(),
5020 value: ANFValue::LoadParam { name: "b".to_string() },
5021 },
5022 ANFBinding {
5023 name: "t2".to_string(),
5024 value: ANFValue::BinOp {
5025 op: "+".to_string(),
5026 left: "t0".to_string(),
5027 right: "t1".to_string(),
5028 result_type: Some("bytes".to_string()), },
5030 },
5031 ANFBinding {
5032 name: "t3".to_string(),
5033 value: ANFValue::LoadParam { name: "expected".to_string() },
5034 },
5035 ANFBinding {
5036 name: "t4".to_string(),
5037 value: ANFValue::BinOp {
5038 op: "===".to_string(),
5039 left: "t2".to_string(),
5040 right: "t3".to_string(),
5041 result_type: Some("bytes".to_string()),
5042 },
5043 },
5044 ANFBinding {
5045 name: "t5".to_string(),
5046 value: ANFValue::Assert { value: "t4".to_string() },
5047 },
5048 ],
5049 is_public: true,
5050 }],
5051 };
5052
5053 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5054 let opcodes = collect_all_opcodes(&methods[0].ops);
5055
5056 assert!(
5057 opcodes.contains(&"OP_CAT".to_string()),
5058 "ByteString '+' (result_type='bytes') should emit OP_CAT; got opcodes: {:?}",
5059 opcodes
5060 );
5061 assert!(
5062 !opcodes.contains(&"OP_ADD".to_string()),
5063 "ByteString '+' should NOT emit OP_ADD (that's for bigint); got opcodes: {:?}",
5064 opcodes
5065 );
5066 }
5067
5068 #[test]
5074 fn test_log2_emits_64_if_ops() {
5075 let program = ANFProgram {
5076 contract_name: "TestLog2Count".to_string(),
5077 properties: vec![],
5078 methods: vec![ANFMethod {
5079 name: "check".to_string(),
5080 params: vec![
5081 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
5082 ],
5083 body: vec![
5084 ANFBinding {
5085 name: "t0".to_string(),
5086 value: ANFValue::LoadParam { name: "n".to_string() },
5087 },
5088 ANFBinding {
5089 name: "t1".to_string(),
5090 value: ANFValue::Call {
5091 func: "log2".to_string(),
5092 args: vec!["t0".to_string()],
5093 },
5094 },
5095 ANFBinding {
5096 name: "t2".to_string(),
5097 value: ANFValue::LoadConst {
5098 value: serde_json::Value::Number(serde_json::Number::from(0)),
5099 },
5100 },
5101 ANFBinding {
5102 name: "t3".to_string(),
5103 value: ANFValue::BinOp {
5104 op: ">=".to_string(),
5105 left: "t1".to_string(),
5106 right: "t2".to_string(),
5107 result_type: None,
5108 },
5109 },
5110 ANFBinding {
5111 name: "t4".to_string(),
5112 value: ANFValue::Assert { value: "t3".to_string() },
5113 },
5114 ],
5115 is_public: true,
5116 }],
5117 };
5118
5119 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5120
5121 fn count_if_ops(ops: &[StackOp]) -> usize {
5123 let mut count = 0;
5124 for op in ops {
5125 match op {
5126 StackOp::If { then_ops, else_ops } => {
5127 count += 1;
5128 count += count_if_ops(then_ops);
5129 count += count_if_ops(else_ops);
5130 }
5131 _ => {}
5132 }
5133 }
5134 count
5135 }
5136
5137 let if_count = count_if_ops(&methods[0].ops);
5138 assert_eq!(
5139 if_count, 64,
5140 "log2 should emit exactly 64 if-ops (one per bit); got {} if-ops",
5141 if_count
5142 );
5143 }
5144}