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 swap(&mut self) {
201 let n = self.slots.len();
202 assert!(n >= 2, "stack underflow on swap");
203 self.slots.swap(n - 1, n - 2);
204 }
205
206 fn dup(&mut self) {
207 assert!(!self.slots.is_empty(), "stack underflow on dup");
208 let top = self.slots.last().unwrap().clone();
209 self.slots.push(top);
210 }
211
212 fn named_slots(&self) -> HashSet<String> {
214 self.slots.iter().filter(|s| !s.is_empty()).cloned().collect()
215 }
216}
217
218fn compute_last_uses(bindings: &[ANFBinding]) -> HashMap<String, usize> {
223 let mut last_use = HashMap::new();
224 for (i, binding) in bindings.iter().enumerate() {
225 for r in collect_refs(&binding.value) {
226 last_use.insert(r, i);
227 }
228 }
229 last_use
230}
231
232fn collect_refs(value: &ANFValue) -> Vec<String> {
233 let mut refs = Vec::new();
234 match value {
235 ANFValue::LoadParam { name } => {
236 refs.push(name.clone());
239 }
240 ANFValue::LoadProp { .. }
241 | ANFValue::GetStateScript { .. } => {}
242
243 ANFValue::LoadConst { value: v } => {
244 if let Some(s) = v.as_str() {
246 if s.len() > 5 && &s[..5] == "@ref:" {
247 refs.push(s[5..].to_string());
248 }
249 }
250 }
251
252 ANFValue::BinOp { left, right, .. } => {
253 refs.push(left.clone());
254 refs.push(right.clone());
255 }
256 ANFValue::UnaryOp { operand, .. } => {
257 refs.push(operand.clone());
258 }
259 ANFValue::Call { args, .. } => {
260 refs.extend(args.iter().cloned());
261 }
262 ANFValue::MethodCall { object, args, .. } => {
263 refs.push(object.clone());
264 refs.extend(args.iter().cloned());
265 }
266 ANFValue::If {
267 cond,
268 then,
269 else_branch,
270 } => {
271 refs.push(cond.clone());
272 for b in then {
273 refs.extend(collect_refs(&b.value));
274 }
275 for b in else_branch {
276 refs.extend(collect_refs(&b.value));
277 }
278 }
279 ANFValue::Loop { body, .. } => {
280 for b in body {
281 refs.extend(collect_refs(&b.value));
282 }
283 }
284 ANFValue::Assert { value } => {
285 refs.push(value.clone());
286 }
287 ANFValue::UpdateProp { value, .. } => {
288 refs.push(value.clone());
289 }
290 ANFValue::CheckPreimage { preimage } => {
291 refs.push(preimage.clone());
292 }
293 ANFValue::DeserializeState { preimage } => {
294 refs.push(preimage.clone());
295 }
296 ANFValue::AddOutput { satoshis, state_values, preimage } => {
297 refs.push(satoshis.clone());
298 refs.extend(state_values.iter().cloned());
299 if !preimage.is_empty() {
300 refs.push(preimage.clone());
301 }
302 }
303 }
304 refs
305}
306
307struct LoweringContext {
312 sm: StackMap,
313 ops: Vec<StackOp>,
314 max_depth: usize,
315 properties: Vec<ANFProperty>,
316 private_methods: HashMap<String, ANFMethod>,
317 local_bindings: HashSet<String>,
320 outer_protected_refs: Option<HashSet<String>>,
322}
323
324impl LoweringContext {
325 fn new(params: &[String], properties: &[ANFProperty]) -> Self {
326 let mut ctx = LoweringContext {
327 sm: StackMap::new(params),
328 ops: Vec::new(),
329 max_depth: 0,
330 properties: properties.to_vec(),
331 private_methods: HashMap::new(),
332 local_bindings: HashSet::new(),
333 outer_protected_refs: None,
334 };
335 ctx.track_depth();
336 ctx
337 }
338
339 fn track_depth(&mut self) {
340 if self.sm.depth() > self.max_depth {
341 self.max_depth = self.sm.depth();
342 }
343 }
344
345 fn emit_op(&mut self, op: StackOp) {
346 self.ops.push(op);
347 self.track_depth();
348 }
349
350 fn is_last_use(&self, name: &str, current_index: usize, last_uses: &HashMap<String, usize>) -> bool {
351 match last_uses.get(name) {
352 None => true,
353 Some(&last) => last <= current_index,
354 }
355 }
356
357 fn bring_to_top(&mut self, name: &str, consume: bool) {
358 let depth = self
359 .sm
360 .find_depth(name)
361 .unwrap_or_else(|| panic!("value '{}' not found on stack", name));
362
363 if depth == 0 {
364 if !consume {
365 self.emit_op(StackOp::Dup);
366 self.sm.dup();
367 }
368 return;
369 }
370
371 if depth == 1 && consume {
372 self.emit_op(StackOp::Swap);
373 self.sm.swap();
374 return;
375 }
376
377 if consume {
378 if depth == 2 {
379 self.emit_op(StackOp::Rot);
380 let removed = self.sm.remove_at_depth(2);
381 self.sm.push(&removed);
382 } else {
383 self.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
384 self.sm.push(""); self.emit_op(StackOp::Roll { depth });
386 self.sm.pop(); let rolled = self.sm.remove_at_depth(depth);
388 self.sm.push(&rolled);
389 }
390 } else {
391 if depth == 1 {
392 self.emit_op(StackOp::Over);
393 let picked = self.sm.peek_at_depth(1).to_string();
394 self.sm.push(&picked);
395 } else {
396 self.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
397 self.sm.push(""); self.emit_op(StackOp::Pick { depth });
399 self.sm.pop(); let picked = self.sm.peek_at_depth(depth).to_string();
401 self.sm.push(&picked);
402 }
403 }
404
405 self.track_depth();
406 }
407
408 fn lower_bindings(&mut self, bindings: &[ANFBinding], terminal_assert: bool) {
413 self.local_bindings = bindings.iter().map(|b| b.name.clone()).collect();
414 let mut last_uses = compute_last_uses(bindings);
415
416 if let Some(ref protected) = self.outer_protected_refs {
418 for r in protected {
419 last_uses.insert(r.clone(), bindings.len());
420 }
421 }
422
423 let mut last_assert_idx: isize = -1;
427 let mut terminal_if_idx: isize = -1;
428 if terminal_assert {
429 let last_binding = bindings.last();
430 if let Some(b) = last_binding {
431 if matches!(&b.value, ANFValue::If { .. }) {
432 terminal_if_idx = (bindings.len() - 1) as isize;
433 } else {
434 for i in (0..bindings.len()).rev() {
435 if matches!(&bindings[i].value, ANFValue::Assert { .. }) {
436 last_assert_idx = i as isize;
437 break;
438 }
439 }
440 }
441 }
442 }
443
444 for (i, binding) in bindings.iter().enumerate() {
445 if matches!(&binding.value, ANFValue::Assert { .. }) && i as isize == last_assert_idx {
446 if let ANFValue::Assert { value } = &binding.value {
448 self.lower_assert(value, i, &last_uses, true);
449 }
450 } else if matches!(&binding.value, ANFValue::If { .. }) && i as isize == terminal_if_idx {
451 if let ANFValue::If { cond, then, else_branch } = &binding.value {
453 self.lower_if(&binding.name, cond, then, else_branch, i, &last_uses, true);
454 }
455 } else {
456 self.lower_binding(binding, i, &last_uses);
457 }
458 }
459 }
460
461 fn lower_binding(
462 &mut self,
463 binding: &ANFBinding,
464 binding_index: usize,
465 last_uses: &HashMap<String, usize>,
466 ) {
467 let name = &binding.name;
468 match &binding.value {
469 ANFValue::LoadParam {
470 name: param_name, ..
471 } => {
472 self.lower_load_param(name, param_name, binding_index, last_uses);
473 }
474 ANFValue::LoadProp {
475 name: prop_name, ..
476 } => {
477 self.lower_load_prop(name, prop_name);
478 }
479 ANFValue::LoadConst { .. } => {
480 self.lower_load_const(name, &binding.value, binding_index, last_uses);
481 }
482 ANFValue::BinOp {
483 op, left, right, result_type, ..
484 } => {
485 self.lower_bin_op(name, op, left, right, binding_index, last_uses, result_type.as_deref());
486 }
487 ANFValue::UnaryOp { op, operand } => {
488 self.lower_unary_op(name, op, operand, binding_index, last_uses);
489 }
490 ANFValue::Call {
491 func: func_name,
492 args,
493 } => {
494 self.lower_call(name, func_name, args, binding_index, last_uses);
495 }
496 ANFValue::MethodCall {
497 object,
498 method,
499 args,
500 } => {
501 self.lower_method_call(name, object, method, args, binding_index, last_uses);
502 }
503 ANFValue::If {
504 cond,
505 then,
506 else_branch,
507 } => {
508 self.lower_if(name, cond, then, else_branch, binding_index, last_uses, false);
509 }
510 ANFValue::Loop {
511 count,
512 body,
513 iter_var,
514 } => {
515 self.lower_loop(name, *count, body, iter_var);
516 }
517 ANFValue::Assert { value } => {
518 self.lower_assert(value, binding_index, last_uses, false);
519 }
520 ANFValue::UpdateProp {
521 name: prop_name,
522 value,
523 } => {
524 self.lower_update_prop(prop_name, value, binding_index, last_uses);
525 }
526 ANFValue::GetStateScript {} => {
527 self.lower_get_state_script(name);
528 }
529 ANFValue::CheckPreimage { preimage } => {
530 self.lower_check_preimage(name, preimage, binding_index, last_uses);
531 }
532 ANFValue::DeserializeState { preimage } => {
533 self.lower_deserialize_state(preimage, binding_index, last_uses);
534 }
535 ANFValue::AddOutput { satoshis, state_values, preimage } => {
536 self.lower_add_output(name, satoshis, state_values, preimage, binding_index, last_uses);
537 }
538 }
539 }
540
541 fn lower_load_param(
546 &mut self,
547 binding_name: &str,
548 param_name: &str,
549 binding_index: usize,
550 last_uses: &HashMap<String, usize>,
551 ) {
552 if self.sm.has(param_name) {
553 let is_last = self.is_last_use(param_name, binding_index, last_uses);
554 self.bring_to_top(param_name, is_last);
555 self.sm.pop();
556 self.sm.push(binding_name);
557 } else {
558 self.emit_op(StackOp::Push(PushValue::Int(0)));
559 self.sm.push(binding_name);
560 }
561 }
562
563 fn lower_load_prop(&mut self, binding_name: &str, prop_name: &str) {
564 let prop = self.properties.iter().find(|p| p.name == prop_name).cloned();
565
566 if self.sm.has(prop_name) {
567 self.bring_to_top(prop_name, false);
571 self.sm.pop();
572 } else if let Some(ref p) = prop {
573 if let Some(ref val) = p.initial_value {
574 self.push_json_value(val);
575 } else {
576 let param_index = self
579 .properties
580 .iter()
581 .position(|p2| p2.name == prop_name)
582 .unwrap_or(0);
583 self.emit_op(StackOp::Placeholder {
584 param_index,
585 param_name: prop_name.to_string(),
586 });
587 }
588 } else {
589 let param_index = self
591 .properties
592 .iter()
593 .position(|p2| p2.name == prop_name)
594 .unwrap_or(0);
595 self.emit_op(StackOp::Placeholder {
596 param_index,
597 param_name: prop_name.to_string(),
598 });
599 }
600 self.sm.push(binding_name);
601 }
602
603 fn push_json_value(&mut self, val: &serde_json::Value) {
604 match val {
605 serde_json::Value::Bool(b) => {
606 self.emit_op(StackOp::Push(PushValue::Bool(*b)));
607 }
608 serde_json::Value::Number(n) => {
609 let i = n.as_i64().map(|v| v as i128).unwrap_or(0);
610 self.emit_op(StackOp::Push(PushValue::Int(i)));
611 }
612 serde_json::Value::String(s) => {
613 let bytes = hex_to_bytes(s);
614 self.emit_op(StackOp::Push(PushValue::Bytes(bytes)));
615 }
616 _ => {
617 self.emit_op(StackOp::Push(PushValue::Int(0)));
618 }
619 }
620 }
621
622 fn lower_load_const(&mut self, binding_name: &str, value: &ANFValue, binding_index: usize, last_uses: &HashMap<String, usize>) {
623 if let Some(ConstValue::Str(ref s)) = value.const_value() {
628 if s.len() > 5 && &s[..5] == "@ref:" {
629 let ref_name = &s[5..];
630 if self.sm.has(ref_name) {
631 let consume = self.local_bindings.contains(ref_name)
636 && self.is_last_use(ref_name, binding_index, last_uses);
637 self.bring_to_top(ref_name, consume);
638 self.sm.pop();
639 self.sm.push(binding_name);
640 } else {
641 self.emit_op(StackOp::Push(PushValue::Int(0)));
643 self.sm.push(binding_name);
644 }
645 return;
646 }
647 if s == "@this" {
649 self.emit_op(StackOp::Push(PushValue::Int(0)));
650 self.sm.push(binding_name);
651 return;
652 }
653 }
654
655 match value.const_value() {
656 Some(ConstValue::Bool(b)) => {
657 self.emit_op(StackOp::Push(PushValue::Bool(b)));
658 }
659 Some(ConstValue::Int(n)) => {
660 self.emit_op(StackOp::Push(PushValue::Int(n)));
661 }
662 Some(ConstValue::Str(s)) => {
663 let bytes = hex_to_bytes(&s);
664 self.emit_op(StackOp::Push(PushValue::Bytes(bytes)));
665 }
666 None => {
667 self.emit_op(StackOp::Push(PushValue::Int(0)));
668 }
669 }
670 self.sm.push(binding_name);
671 }
672
673 fn lower_bin_op(
674 &mut self,
675 binding_name: &str,
676 op: &str,
677 left: &str,
678 right: &str,
679 binding_index: usize,
680 last_uses: &HashMap<String, usize>,
681 result_type: Option<&str>,
682 ) {
683 let left_is_last = self.is_last_use(left, binding_index, last_uses);
684 self.bring_to_top(left, left_is_last);
685
686 let right_is_last = self.is_last_use(right, binding_index, last_uses);
687 self.bring_to_top(right, right_is_last);
688
689 self.sm.pop();
690 self.sm.pop();
691
692 if result_type == Some("bytes") && (op == "===" || op == "!==") {
694 self.emit_op(StackOp::Opcode("OP_EQUAL".to_string()));
695 if op == "!==" {
696 self.emit_op(StackOp::Opcode("OP_NOT".to_string()));
697 }
698 } else {
699 let codes = binop_opcodes(op)
700 .unwrap_or_else(|| panic!("unknown binary operator: {}", op));
701 for code in codes {
702 self.emit_op(StackOp::Opcode(code.to_string()));
703 }
704 }
705
706 self.sm.push(binding_name);
707 self.track_depth();
708 }
709
710 fn lower_unary_op(
711 &mut self,
712 binding_name: &str,
713 op: &str,
714 operand: &str,
715 binding_index: usize,
716 last_uses: &HashMap<String, usize>,
717 ) {
718 let is_last = self.is_last_use(operand, binding_index, last_uses);
719 self.bring_to_top(operand, is_last);
720
721 self.sm.pop();
722
723 let codes = unaryop_opcodes(op)
724 .unwrap_or_else(|| panic!("unknown unary operator: {}", op));
725 for code in codes {
726 self.emit_op(StackOp::Opcode(code.to_string()));
727 }
728
729 self.sm.push(binding_name);
730 self.track_depth();
731 }
732
733 fn lower_call(
734 &mut self,
735 binding_name: &str,
736 func_name: &str,
737 args: &[String],
738 binding_index: usize,
739 last_uses: &HashMap<String, usize>,
740 ) {
741 if func_name == "assert" {
743 if !args.is_empty() {
744 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
745 self.bring_to_top(&args[0], is_last);
746 self.sm.pop();
747 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
748 self.sm.push(binding_name);
749 }
750 return;
751 }
752
753 if func_name == "super" {
756 self.sm.push(binding_name);
757 return;
758 }
759
760 if func_name == "__array_access" {
761 self.lower_array_access(binding_name, args, binding_index, last_uses);
762 return;
763 }
764
765 if func_name == "reverseBytes" {
766 self.lower_reverse_bytes(binding_name, args, binding_index, last_uses);
767 return;
768 }
769
770 if func_name == "substr" {
771 self.lower_substr(binding_name, args, binding_index, last_uses);
772 return;
773 }
774
775 if func_name == "verifyRabinSig" {
776 self.lower_verify_rabin_sig(binding_name, args, binding_index, last_uses);
777 return;
778 }
779
780 if func_name == "verifyWOTS" {
781 self.lower_verify_wots(binding_name, args, binding_index, last_uses);
782 return;
783 }
784
785 if func_name.starts_with("verifySLHDSA_") {
786 let param_key = func_name.trim_start_matches("verifySLHDSA_");
787 self.lower_verify_slh_dsa(binding_name, param_key, args, binding_index, last_uses);
788 return;
789 }
790
791 if is_ec_builtin(func_name) {
792 self.lower_ec_builtin(binding_name, func_name, args, binding_index, last_uses);
793 return;
794 }
795
796 if func_name == "safediv" {
797 self.lower_safediv(binding_name, args, binding_index, last_uses);
798 return;
799 }
800
801 if func_name == "safemod" {
802 self.lower_safemod(binding_name, args, binding_index, last_uses);
803 return;
804 }
805
806 if func_name == "clamp" {
807 self.lower_clamp(binding_name, args, binding_index, last_uses);
808 return;
809 }
810
811 if func_name == "pow" {
812 self.lower_pow(binding_name, args, binding_index, last_uses);
813 return;
814 }
815
816 if func_name == "mulDiv" {
817 self.lower_mul_div(binding_name, args, binding_index, last_uses);
818 return;
819 }
820
821 if func_name == "percentOf" {
822 self.lower_percent_of(binding_name, args, binding_index, last_uses);
823 return;
824 }
825
826 if func_name == "sqrt" {
827 self.lower_sqrt(binding_name, args, binding_index, last_uses);
828 return;
829 }
830
831 if func_name == "gcd" {
832 self.lower_gcd(binding_name, args, binding_index, last_uses);
833 return;
834 }
835
836 if func_name == "divmod" {
837 self.lower_divmod(binding_name, args, binding_index, last_uses);
838 return;
839 }
840
841 if func_name == "log2" {
842 self.lower_log2(binding_name, args, binding_index, last_uses);
843 return;
844 }
845
846 if func_name == "sign" {
847 self.lower_sign(binding_name, args, binding_index, last_uses);
848 return;
849 }
850
851 if func_name == "right" {
852 self.lower_right(binding_name, args, binding_index, last_uses);
853 return;
854 }
855
856 if func_name == "pack" || func_name == "toByteString" {
859 if !args.is_empty() {
860 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
861 self.bring_to_top(&args[0], is_last);
862 self.sm.pop();
863 }
864 self.sm.push(binding_name);
865 return;
866 }
867
868 if func_name == "computeStateOutputHash" {
871 self.lower_compute_state_output_hash(binding_name, args, binding_index, last_uses);
872 return;
873 }
874
875 if func_name == "computeStateOutput" {
879 self.lower_compute_state_output(binding_name, args, binding_index, last_uses);
880 return;
881 }
882
883 if func_name == "buildChangeOutput" {
887 self.lower_build_change_output(binding_name, args, binding_index, last_uses);
888 return;
889 }
890
891 if func_name.starts_with("extract") {
895 self.lower_extractor(binding_name, func_name, args, binding_index, last_uses);
896 return;
897 }
898
899 for arg in args {
901 let is_last = self.is_last_use(arg, binding_index, last_uses);
902 self.bring_to_top(arg, is_last);
903 }
904
905 for _ in args {
906 self.sm.pop();
907 }
908
909 if let Some(codes) = builtin_opcodes(func_name) {
910 for code in codes {
911 self.emit_op(StackOp::Opcode(code.to_string()));
912 }
913 } else {
914 self.emit_op(StackOp::Push(PushValue::Int(0)));
916 self.sm.push(binding_name);
917 return;
918 }
919
920 if func_name == "split" {
921 self.sm.push("");
922 self.sm.push(binding_name);
923 } else if func_name == "len" {
924 self.sm.push("");
925 self.sm.push(binding_name);
926 } else {
927 self.sm.push(binding_name);
928 }
929
930 self.track_depth();
931 }
932
933 fn lower_method_call(
934 &mut self,
935 binding_name: &str,
936 _object: &str,
937 method: &str,
938 args: &[String],
939 binding_index: usize,
940 last_uses: &HashMap<String, usize>,
941 ) {
942 if method == "getStateScript" {
943 self.lower_get_state_script(binding_name);
944 return;
945 }
946
947 if let Some(private_method) = self.private_methods.get(method).cloned() {
949 self.inline_method_call(binding_name, &private_method, args, binding_index, last_uses);
950 return;
951 }
952
953 self.lower_call(binding_name, method, args, binding_index, last_uses);
955 }
956
957 fn inline_method_call(
960 &mut self,
961 binding_name: &str,
962 method: &ANFMethod,
963 args: &[String],
964 binding_index: usize,
965 last_uses: &HashMap<String, usize>,
966 ) {
967 for (i, arg) in args.iter().enumerate() {
969 if i < method.params.len() {
970 let is_last = self.is_last_use(arg, binding_index, last_uses);
971 self.bring_to_top(arg, is_last);
972 self.sm.pop();
974 self.sm.push(&method.params[i].name);
975 }
976 }
977
978 self.lower_bindings(&method.body, false);
980
981 if !method.body.is_empty() {
984 let last_binding_name = &method.body[method.body.len() - 1].name;
985 if self.sm.depth() > 0 {
986 let top_name = self.sm.peek_at_depth(0).to_string();
987 if top_name == *last_binding_name {
988 self.sm.pop();
989 self.sm.push(binding_name);
990 }
991 }
992 }
993 }
994
995 fn lower_if(
996 &mut self,
997 binding_name: &str,
998 cond: &str,
999 then_bindings: &[ANFBinding],
1000 else_bindings: &[ANFBinding],
1001 binding_index: usize,
1002 last_uses: &HashMap<String, usize>,
1003 terminal_assert: bool,
1004 ) {
1005 let is_last = self.is_last_use(cond, binding_index, last_uses);
1006 self.bring_to_top(cond, is_last);
1007 self.sm.pop(); let mut protected_refs = HashSet::new();
1011 for (ref_name, &last_idx) in last_uses.iter() {
1012 if last_idx > binding_index && self.sm.has(ref_name) {
1013 protected_refs.insert(ref_name.clone());
1014 }
1015 }
1016
1017 let pre_if_names = self.sm.named_slots();
1019
1020 let mut then_ctx = LoweringContext::new(&[], &self.properties);
1022 then_ctx.sm = self.sm.clone();
1023 then_ctx.outer_protected_refs = Some(protected_refs.clone());
1024 then_ctx.lower_bindings(then_bindings, terminal_assert);
1025
1026 if terminal_assert && then_ctx.sm.depth() > 1 {
1027 let excess = then_ctx.sm.depth() - 1;
1028 for _ in 0..excess {
1029 then_ctx.emit_op(StackOp::Nip);
1030 then_ctx.sm.remove_at_depth(1);
1031 }
1032 }
1033
1034 let mut else_ctx = LoweringContext::new(&[], &self.properties);
1036 else_ctx.sm = self.sm.clone();
1037 else_ctx.outer_protected_refs = Some(protected_refs);
1038 else_ctx.lower_bindings(else_bindings, terminal_assert);
1039
1040 if terminal_assert && else_ctx.sm.depth() > 1 {
1041 let excess = else_ctx.sm.depth() - 1;
1042 for _ in 0..excess {
1043 else_ctx.emit_op(StackOp::Nip);
1044 else_ctx.sm.remove_at_depth(1);
1045 }
1046 }
1047
1048 let post_then_names = then_ctx.sm.named_slots();
1059 let mut consumed_names: Vec<String> = Vec::new();
1060 for name in &pre_if_names {
1061 if !post_then_names.contains(name) && else_ctx.sm.has(name) {
1062 consumed_names.push(name.clone());
1063 }
1064 }
1065 if !consumed_names.is_empty() {
1066 let mut depths: Vec<usize> = consumed_names
1068 .iter()
1069 .map(|n| else_ctx.sm.find_depth(n).unwrap())
1070 .collect();
1071 depths.sort_by(|a, b| b.cmp(a));
1072 for depth in depths {
1073 if depth == 0 {
1074 else_ctx.emit_op(StackOp::Drop);
1075 else_ctx.sm.pop();
1076 } else if depth == 1 {
1077 else_ctx.emit_op(StackOp::Nip);
1078 else_ctx.sm.remove_at_depth(1);
1079 } else {
1080 else_ctx.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
1082 else_ctx.sm.push("");
1083 else_ctx.emit_op(StackOp::Roll { depth });
1084 else_ctx.sm.pop(); let rolled = else_ctx.sm.remove_at_depth(depth);
1086 else_ctx.sm.push(&rolled);
1087 else_ctx.emit_op(StackOp::Drop);
1088 else_ctx.sm.pop();
1089 }
1090 }
1091 else_ctx.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1093 else_ctx.sm.push("");
1094 }
1095 let post_else_names = else_ctx.sm.named_slots();
1097 let mut else_consumed_names: Vec<String> = Vec::new();
1098 for name in &pre_if_names {
1099 if !post_else_names.contains(name) && then_ctx.sm.has(name) {
1100 else_consumed_names.push(name.clone());
1101 }
1102 }
1103 if !else_consumed_names.is_empty() {
1104 let mut depths: Vec<usize> = else_consumed_names
1105 .iter()
1106 .map(|n| then_ctx.sm.find_depth(n).unwrap())
1107 .collect();
1108 depths.sort_by(|a, b| b.cmp(a));
1109 for depth in depths {
1110 if depth == 0 {
1111 then_ctx.emit_op(StackOp::Drop);
1112 then_ctx.sm.pop();
1113 } else if depth == 1 {
1114 then_ctx.emit_op(StackOp::Nip);
1115 then_ctx.sm.remove_at_depth(1);
1116 } else {
1117 then_ctx.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
1118 then_ctx.sm.push("");
1119 then_ctx.emit_op(StackOp::Roll { depth });
1120 then_ctx.sm.pop();
1121 let rolled = then_ctx.sm.remove_at_depth(depth);
1122 then_ctx.sm.push(&rolled);
1123 then_ctx.emit_op(StackOp::Drop);
1124 then_ctx.sm.pop();
1125 }
1126 }
1127 then_ctx.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1128 then_ctx.sm.push("");
1129 }
1130
1131 let then_ops = then_ctx.ops;
1132 let else_ops = else_ctx.ops;
1133
1134 self.emit_op(StackOp::If {
1135 then_ops,
1136 else_ops: if else_ops.is_empty() {
1137 Vec::new()
1138 } else {
1139 else_ops
1140 },
1141 });
1142
1143 let post_branch_names = then_ctx.sm.named_slots();
1145 for name in &pre_if_names {
1146 if !post_branch_names.contains(name) && self.sm.has(name) {
1147 if let Some(depth) = self.sm.find_depth(name) {
1148 self.sm.remove_at_depth(depth);
1149 }
1150 }
1151 }
1152
1153 self.sm.push(binding_name);
1154 self.track_depth();
1155
1156 if then_ctx.max_depth > self.max_depth {
1157 self.max_depth = then_ctx.max_depth;
1158 }
1159 if else_ctx.max_depth > self.max_depth {
1160 self.max_depth = else_ctx.max_depth;
1161 }
1162 }
1163
1164 fn lower_loop(
1165 &mut self,
1166 _binding_name: &str,
1167 count: usize,
1168 body: &[ANFBinding],
1169 iter_var: &str,
1170 ) {
1171 let body_binding_names: HashSet<String> = body.iter().map(|b| b.name.clone()).collect();
1174 let mut outer_refs = HashSet::new();
1175 for b in body {
1176 if let ANFValue::LoadParam { name } = &b.value {
1177 if name != iter_var {
1178 outer_refs.insert(name.clone());
1179 }
1180 }
1181 if let ANFValue::LoadConst { value: v } = &b.value {
1183 if let Some(s) = v.as_str() {
1184 if s.len() > 5 && &s[..5] == "@ref:" {
1185 let ref_name = &s[5..];
1186 if !body_binding_names.contains(ref_name) {
1187 outer_refs.insert(ref_name.to_string());
1188 }
1189 }
1190 }
1191 }
1192 }
1193
1194 let prev_local_bindings = self.local_bindings.clone();
1197 self.local_bindings = self.local_bindings.union(&body_binding_names).cloned().collect();
1198
1199 for i in 0..count {
1200 self.emit_op(StackOp::Push(PushValue::Int(i as i128)));
1201 self.sm.push(iter_var);
1202
1203 let mut last_uses = compute_last_uses(body);
1204
1205 if i < count - 1 {
1208 for ref_name in &outer_refs {
1209 last_uses.insert(ref_name.clone(), body.len());
1210 }
1211 }
1212
1213 for (j, binding) in body.iter().enumerate() {
1214 self.lower_binding(binding, j, &last_uses);
1215 }
1216
1217 if self.sm.has(iter_var) {
1220 let depth = self.sm.find_depth(iter_var);
1221 if let Some(0) = depth {
1222 self.emit_op(StackOp::Drop);
1223 self.sm.pop();
1224 }
1225 }
1226 }
1227 self.local_bindings = prev_local_bindings;
1229 }
1233
1234 fn lower_assert(
1235 &mut self,
1236 value_ref: &str,
1237 binding_index: usize,
1238 last_uses: &HashMap<String, usize>,
1239 terminal: bool,
1240 ) {
1241 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1242 self.bring_to_top(value_ref, is_last);
1243 if terminal {
1244 } else {
1247 self.sm.pop();
1248 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
1249 }
1250 self.track_depth();
1251 }
1252
1253 fn lower_update_prop(
1254 &mut self,
1255 prop_name: &str,
1256 value_ref: &str,
1257 binding_index: usize,
1258 last_uses: &HashMap<String, usize>,
1259 ) {
1260 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1261 self.bring_to_top(value_ref, is_last);
1262 self.sm.pop();
1263 self.sm.push(prop_name);
1264 self.track_depth();
1265 }
1266
1267 fn lower_get_state_script(&mut self, binding_name: &str) {
1268 let state_props: Vec<ANFProperty> = self
1269 .properties
1270 .iter()
1271 .filter(|p| !p.readonly)
1272 .cloned()
1273 .collect();
1274
1275 if state_props.is_empty() {
1276 self.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1277 self.sm.push(binding_name);
1278 return;
1279 }
1280
1281 let mut first = true;
1282 for prop in &state_props {
1283 if self.sm.has(&prop.name) {
1284 self.bring_to_top(&prop.name, true); } else if let Some(ref val) = prop.initial_value {
1286 self.push_json_value(val);
1287 self.sm.push("");
1288 } else {
1289 self.emit_op(StackOp::Push(PushValue::Int(0)));
1290 self.sm.push("");
1291 }
1292
1293 if prop.prop_type == "bigint" {
1295 self.emit_op(StackOp::Push(PushValue::Int(8)));
1296 self.sm.push("");
1297 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1298 self.sm.pop(); } else if prop.prop_type == "boolean" {
1300 self.emit_op(StackOp::Push(PushValue::Int(1)));
1301 self.sm.push("");
1302 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1303 self.sm.pop(); }
1305 if !first {
1308 self.sm.pop();
1309 self.sm.pop();
1310 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
1311 self.sm.push("");
1312 }
1313 first = false;
1314 }
1315
1316 self.sm.pop();
1317 self.sm.push(binding_name);
1318 self.track_depth();
1319 }
1320
1321 fn lower_compute_state_output_hash(
1326 &mut self,
1327 binding_name: &str,
1328 args: &[String],
1329 binding_index: usize,
1330 last_uses: &std::collections::HashMap<String, usize>,
1331 ) {
1332 let mut state_len: usize = 0;
1334 for p in &self.properties {
1335 if p.readonly {
1336 continue;
1337 }
1338 state_len += match p.prop_type.as_str() {
1339 "bigint" => 8,
1340 "boolean" => 1,
1341 "PubKey" => 33,
1342 "Addr" => 20,
1343 "Sha256" => 32,
1344 "Point" => 64,
1345 _ => panic!(
1346 "computeStateOutputHash: unsupported mutable property type '{}' for '{}'",
1347 p.prop_type, p.name
1348 ),
1349 };
1350 }
1351
1352 let preimage_ref = &args[0];
1353 let state_bytes_ref = &args[1];
1354
1355 let sb_last = self.is_last_use(state_bytes_ref, binding_index, last_uses);
1357 self.bring_to_top(state_bytes_ref, sb_last);
1358 let pre_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1359 self.bring_to_top(preimage_ref, pre_last);
1360
1361 self.emit_op(StackOp::Push(PushValue::Int(104)));
1365 self.sm.push("");
1366 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip); self.sm.pop();
1373 self.sm.pop();
1374 self.sm.push(""); self.emit_op(StackOp::Opcode("OP_SIZE".into())); self.sm.push(""); self.emit_op(StackOp::Push(PushValue::Int(44)));
1380 self.sm.push("");
1381 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1382 self.sm.pop();
1383 self.sm.pop();
1384 self.sm.push("");
1385 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
1391 self.sm.pop();
1392
1393 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1395 self.sm.push("");
1396 self.emit_op(StackOp::Push(PushValue::Int(8)));
1397 self.sm.push("");
1398 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1399 self.sm.pop();
1400 self.sm.pop();
1401 self.sm.push("");
1402 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
1408 self.sm.pop();
1409
1410 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1412 self.sm.push("");
1413 self.emit_op(StackOp::Push(PushValue::Int((state_len + 1) as i128)));
1414 self.sm.push("");
1415 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1416 self.sm.pop();
1417 self.sm.pop();
1418 self.sm.push("");
1419 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
1425 self.sm.pop();
1426
1427 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1429 self.sm.push("");
1430 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1431 self.sm.pop();
1432 self.sm.pop();
1433 self.sm.push("");
1434
1435 self.emit_op(StackOp::Swap);
1437 self.sm.swap();
1438 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1439 self.sm.pop();
1440 self.sm.pop();
1441 self.sm.push("");
1442
1443 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
1445 self.sm.push("");
1446 self.emit_op(StackOp::Swap);
1447 self.sm.swap();
1448 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1449 self.sm.pop();
1450 self.sm.pop();
1451 self.sm.push("");
1452
1453 self.emit_op(StackOp::Opcode("OP_HASH256".into()));
1455
1456 self.sm.pop();
1457 self.sm.push(binding_name);
1458 self.track_depth();
1459 }
1460
1461 fn lower_compute_state_output(
1465 &mut self,
1466 binding_name: &str,
1467 args: &[String],
1468 binding_index: usize,
1469 last_uses: &std::collections::HashMap<String, usize>,
1470 ) {
1471 let mut state_len: usize = 0;
1473 for p in &self.properties {
1474 if p.readonly {
1475 continue;
1476 }
1477 state_len += match p.prop_type.as_str() {
1478 "bigint" => 8,
1479 "boolean" => 1,
1480 "PubKey" => 33,
1481 "Addr" => 20,
1482 "Sha256" => 32,
1483 "Point" => 64,
1484 _ => panic!(
1485 "computeStateOutput: unsupported mutable property type '{}' for '{}'",
1486 p.prop_type, p.name
1487 ),
1488 };
1489 }
1490
1491 let preimage_ref = &args[0];
1492 let state_bytes_ref = &args[1];
1493
1494 let sb_last = self.is_last_use(state_bytes_ref, binding_index, last_uses);
1496 self.bring_to_top(state_bytes_ref, sb_last);
1497 let pre_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1498 self.bring_to_top(preimage_ref, pre_last);
1499
1500 self.emit_op(StackOp::Push(PushValue::Int(104)));
1504 self.sm.push("");
1505 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip); self.sm.pop();
1512 self.sm.pop();
1513 self.sm.push(""); self.emit_op(StackOp::Opcode("OP_SIZE".into())); self.sm.push(""); self.emit_op(StackOp::Push(PushValue::Int(44)));
1519 self.sm.push("");
1520 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1521 self.sm.pop();
1522 self.sm.pop();
1523 self.sm.push("");
1524 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
1530 self.sm.pop();
1531
1532 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1534 self.sm.push("");
1535 self.emit_op(StackOp::Push(PushValue::Int(8)));
1536 self.sm.push("");
1537 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1538 self.sm.pop();
1539 self.sm.pop();
1540 self.sm.push("");
1541 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
1547 self.sm.pop();
1548
1549 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1551 self.sm.push("");
1552 self.emit_op(StackOp::Push(PushValue::Int((state_len + 1) as i128)));
1553 self.sm.push("");
1554 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1555 self.sm.pop();
1556 self.sm.pop();
1557 self.sm.push("");
1558 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop(); self.sm.pop(); self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
1564 self.sm.pop();
1565
1566 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1568 self.sm.push("");
1569 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1570 self.sm.pop();
1571 self.sm.pop();
1572 self.sm.push("");
1573
1574 self.emit_op(StackOp::Swap);
1576 self.sm.swap();
1577 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1578 self.sm.pop();
1579 self.sm.pop();
1580 self.sm.push("");
1581
1582 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
1584 self.sm.push("");
1585 self.emit_op(StackOp::Swap);
1586 self.sm.swap();
1587 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1588 self.sm.pop();
1589 self.sm.pop();
1590 self.sm.push("");
1591 self.sm.pop();
1594 self.sm.push(binding_name);
1595 self.track_depth();
1596 }
1597
1598 fn lower_build_change_output(
1602 &mut self,
1603 binding_name: &str,
1604 args: &[String],
1605 binding_index: usize,
1606 last_uses: &std::collections::HashMap<String, usize>,
1607 ) {
1608 let pkh_ref = &args[0];
1609 let amount_ref = &args[1];
1610
1611 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x19, 0x76, 0xa9, 0x14])));
1614 self.sm.push("");
1615
1616 let pkh_last = self.is_last_use(pkh_ref, binding_index, last_uses);
1618 self.bring_to_top(pkh_ref, pkh_last);
1619 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1621 self.sm.pop();
1622 self.sm.pop();
1623 self.sm.push("");
1624
1625 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x88, 0xac])));
1627 self.sm.push("");
1628 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1630 self.sm.pop();
1631 self.sm.pop();
1632 self.sm.push("");
1633 let amount_last = self.is_last_use(amount_ref, binding_index, last_uses);
1637 self.bring_to_top(amount_ref, amount_last);
1638 self.emit_op(StackOp::Push(PushValue::Int(8)));
1639 self.sm.push("");
1640 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
1641 self.sm.pop(); self.emit_op(StackOp::Swap);
1644 self.sm.swap();
1645 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1647 self.sm.pop();
1648 self.sm.pop();
1649 self.sm.push("");
1650 self.sm.pop();
1653 self.sm.push(binding_name);
1654 self.track_depth();
1655 }
1656
1657 fn lower_add_output(
1658 &mut self,
1659 binding_name: &str,
1660 satoshis: &str,
1661 state_values: &[String],
1662 preimage: &str,
1663 binding_index: usize,
1664 last_uses: &HashMap<String, usize>,
1665 ) {
1666 let state_props: Vec<ANFProperty> = self
1670 .properties
1671 .iter()
1672 .filter(|p| !p.readonly)
1673 .cloned()
1674 .collect();
1675
1676 let mut state_len: usize = 0;
1678 for p in &state_props {
1679 state_len += match p.prop_type.as_str() {
1680 "bigint" => 8,
1681 "boolean" => 1,
1682 "PubKey" => 33,
1683 "Addr" => 20,
1684 "Sha256" => 32,
1685 "Point" => 64,
1686 _ => panic!("addOutput: unsupported mutable property type '{}'", p.prop_type),
1687 };
1688 }
1689
1690 let pre_is_last = self.is_last_use(preimage, binding_index, last_uses);
1692 self.bring_to_top(preimage, pre_is_last);
1693
1694 self.emit_op(StackOp::Push(PushValue::Int(104)));
1696 self.sm.push("");
1697 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
1698 self.sm.pop();
1699 self.sm.pop();
1700 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip);
1703 self.sm.pop();
1704 self.sm.pop();
1705 self.sm.push(""); self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1709 self.sm.push("");
1710 self.emit_op(StackOp::Push(PushValue::Int(44)));
1711 self.sm.push("");
1712 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1713 self.sm.pop();
1714 self.sm.pop();
1715 self.sm.push("");
1716 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
1717 self.sm.pop();
1718 self.sm.pop();
1719 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
1722 self.sm.pop();
1723
1724 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1726 self.sm.push("");
1727 self.emit_op(StackOp::Push(PushValue::Int(8)));
1728 self.sm.push("");
1729 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1730 self.sm.pop();
1731 self.sm.pop();
1732 self.sm.push("");
1733 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
1734 self.sm.pop();
1735 self.sm.pop();
1736 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
1740
1741 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1743 self.sm.push("");
1744 self.emit_op(StackOp::Push(PushValue::Int((state_len + 1) as i128)));
1745 self.sm.push("");
1746 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1747 self.sm.pop();
1748 self.sm.pop();
1749 self.sm.push("");
1750 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
1751 self.sm.pop();
1752 self.sm.pop();
1753 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
1756 self.sm.pop();
1757
1758 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1760 self.sm.push("");
1761 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1762 self.sm.pop();
1763 self.sm.pop();
1764 self.sm.push("");
1765
1766 for (i, value_ref) in state_values.iter().enumerate() {
1768 if i >= state_props.len() {
1769 break;
1770 }
1771 let prop = &state_props[i];
1772
1773 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1774 self.bring_to_top(value_ref, is_last);
1775
1776 if prop.prop_type == "bigint" {
1777 self.emit_op(StackOp::Push(PushValue::Int(8)));
1778 self.sm.push("");
1779 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1780 self.sm.pop();
1781 } else if prop.prop_type == "boolean" {
1782 self.emit_op(StackOp::Push(PushValue::Int(1)));
1783 self.sm.push("");
1784 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1785 self.sm.pop();
1786 }
1787
1788 self.sm.pop();
1789 self.sm.pop();
1790 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
1791 self.sm.push("");
1792 }
1793
1794 let is_last_satoshis = self.is_last_use(satoshis, binding_index, last_uses);
1796 self.bring_to_top(satoshis, is_last_satoshis);
1797 self.emit_op(StackOp::Push(PushValue::Int(8)));
1798 self.sm.push("");
1799 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1800 self.sm.pop();
1801 self.emit_op(StackOp::Swap);
1802 self.sm.swap();
1803 self.sm.pop();
1804 self.sm.pop();
1805 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
1807
1808 self.sm.pop();
1810 self.sm.push(binding_name);
1811 self.track_depth();
1812 }
1813
1814 fn lower_check_preimage(
1815 &mut self,
1816 binding_name: &str,
1817 preimage: &str,
1818 binding_index: usize,
1819 last_uses: &HashMap<String, usize>,
1820 ) {
1821 let is_last = self.is_last_use(preimage, binding_index, last_uses);
1855 self.bring_to_top(preimage, is_last);
1856
1857 self.bring_to_top("_opPushTxSig", true);
1859
1860 let g: Vec<u8> = vec![
1862 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB,
1863 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B,
1864 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28,
1865 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17,
1866 0x98,
1867 ];
1868 self.emit_op(StackOp::Push(PushValue::Bytes(g)));
1869 self.sm.push(""); self.emit_op(StackOp::Opcode("OP_CHECKSIG".to_string()));
1873 self.sm.pop(); self.sm.pop(); self.sm.push(""); self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
1879 self.sm.pop(); self.sm.pop();
1884 self.sm.push(binding_name);
1885
1886 self.track_depth();
1887 }
1888
1889 fn lower_deserialize_state(
1898 &mut self,
1899 preimage_ref: &str,
1900 binding_index: usize,
1901 last_uses: &HashMap<String, usize>,
1902 ) {
1903 let mut prop_names: Vec<String> = Vec::new();
1907 let mut prop_types: Vec<String> = Vec::new();
1908 let mut prop_sizes: Vec<i128> = Vec::new();
1909 let mut state_len: i128 = 0;
1910
1911 for p in &self.properties {
1912 if p.readonly {
1913 continue;
1914 }
1915 prop_names.push(p.name.clone());
1916 prop_types.push(p.prop_type.clone());
1917 let sz: i128 = match p.prop_type.as_str() {
1918 "bigint" => 8,
1919 "boolean" => 1,
1920 "PubKey" => 33,
1921 "Addr" => 20,
1922 "Sha256" => 32,
1923 "Point" => 64,
1924 _ => panic!("deserialize_state: unsupported type: {}", p.prop_type),
1925 };
1926 prop_sizes.push(sz);
1927 state_len += sz;
1928 }
1929
1930 if prop_names.is_empty() {
1931 return;
1932 }
1933
1934 let is_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1936 self.bring_to_top(preimage_ref, is_last);
1937
1938 self.emit_op(StackOp::Push(PushValue::Int(104)));
1940 self.sm.push("");
1941 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
1942 self.sm.pop();
1943 self.sm.pop();
1944 self.sm.push("");
1945 self.sm.push("");
1946 self.emit_op(StackOp::Nip);
1947 self.sm.pop();
1948 self.sm.pop();
1949 self.sm.push("");
1950
1951 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
1953 self.sm.push("");
1954 self.emit_op(StackOp::Push(PushValue::Int(44)));
1955 self.sm.push("");
1956 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
1957 self.sm.pop();
1958 self.sm.pop();
1959 self.sm.push("");
1960 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
1961 self.sm.pop();
1962 self.sm.pop();
1963 self.sm.push("");
1964 self.sm.push("");
1965 self.emit_op(StackOp::Drop);
1966 self.sm.pop();
1967
1968 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
1970 self.sm.push("");
1971 self.emit_op(StackOp::Push(PushValue::Int(8)));
1972 self.sm.push("");
1973 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
1974 self.sm.pop();
1975 self.sm.pop();
1976 self.sm.push("");
1977 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
1978 self.sm.pop();
1979 self.sm.pop();
1980 self.sm.push("");
1981 self.sm.push("");
1982 self.emit_op(StackOp::Drop);
1983 self.sm.pop();
1984
1985 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
1987 self.sm.push("");
1988 self.emit_op(StackOp::Push(PushValue::Int(state_len)));
1989 self.sm.push("");
1990 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
1991 self.sm.pop();
1992 self.sm.pop();
1993 self.sm.push("");
1994 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
1995 self.sm.pop();
1996 self.sm.pop();
1997 self.sm.push("");
1998 self.sm.push("");
1999 self.emit_op(StackOp::Nip);
2000 self.sm.pop();
2001 self.sm.pop();
2002 self.sm.push("");
2003
2004 let num_props = prop_names.len();
2006
2007 if num_props == 1 {
2008 if prop_types[0] == "bigint" || prop_types[0] == "boolean" {
2010 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2011 }
2012 self.sm.pop();
2013 self.sm.push(&prop_names[0]);
2014 } else {
2015 for i in 0..num_props {
2017 let sz = prop_sizes[i];
2018 if i < num_props - 1 {
2019 self.emit_op(StackOp::Push(PushValue::Int(sz)));
2021 self.sm.push("");
2022 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2023 self.sm.pop();
2024 self.sm.pop();
2025 self.sm.push("");
2026 self.sm.push("");
2027 self.emit_op(StackOp::Swap);
2029 self.sm.swap();
2030 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2032 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2033 }
2034 self.emit_op(StackOp::Swap);
2036 self.sm.swap();
2037 self.sm.pop();
2038 self.sm.pop();
2039 self.sm.push(&prop_names[i]);
2040 self.sm.push("");
2041 } else {
2042 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2044 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2045 }
2046 self.sm.pop();
2047 self.sm.push(&prop_names[i]);
2048 }
2049 }
2050 }
2051
2052 self.track_depth();
2053 }
2054
2055 fn lower_extractor(
2073 &mut self,
2074 binding_name: &str,
2075 func_name: &str,
2076 args: &[String],
2077 binding_index: usize,
2078 last_uses: &HashMap<String, usize>,
2079 ) {
2080 assert!(!args.is_empty(), "{} requires 1 argument", func_name);
2081 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
2082 self.bring_to_top(&args[0], is_last);
2083
2084 self.sm.pop(); match func_name {
2088 "extractVersion" => {
2089 self.emit_op(StackOp::Push(PushValue::Int(4)));
2091 self.sm.push("");
2092 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2093 self.sm.pop();
2094 self.sm.push("");
2095 self.sm.push("");
2096 self.emit_op(StackOp::Drop);
2097 self.sm.pop();
2098 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2099 }
2100 "extractHashPrevouts" => {
2101 self.emit_op(StackOp::Push(PushValue::Int(4)));
2103 self.sm.push("");
2104 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2105 self.sm.pop();
2106 self.sm.push("");
2107 self.sm.push("");
2108 self.emit_op(StackOp::Nip);
2109 self.sm.pop();
2110 self.sm.pop();
2111 self.sm.push("");
2112 self.emit_op(StackOp::Push(PushValue::Int(32)));
2113 self.sm.push("");
2114 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2115 self.sm.pop(); self.sm.pop(); self.sm.push("");
2118 self.sm.push("");
2119 self.emit_op(StackOp::Drop);
2120 self.sm.pop();
2121 }
2122 "extractHashSequence" => {
2123 self.emit_op(StackOp::Push(PushValue::Int(36)));
2125 self.sm.push("");
2126 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2127 self.sm.pop();
2128 self.sm.push("");
2129 self.sm.push("");
2130 self.emit_op(StackOp::Nip);
2131 self.sm.pop();
2132 self.sm.pop();
2133 self.sm.push("");
2134 self.emit_op(StackOp::Push(PushValue::Int(32)));
2135 self.sm.push("");
2136 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2137 self.sm.pop(); self.sm.pop(); self.sm.push("");
2140 self.sm.push("");
2141 self.emit_op(StackOp::Drop);
2142 self.sm.pop();
2143 }
2144 "extractOutpoint" => {
2145 self.emit_op(StackOp::Push(PushValue::Int(68)));
2147 self.sm.push("");
2148 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2149 self.sm.pop();
2150 self.sm.push("");
2151 self.sm.push("");
2152 self.emit_op(StackOp::Nip);
2153 self.sm.pop();
2154 self.sm.pop();
2155 self.sm.push("");
2156 self.emit_op(StackOp::Push(PushValue::Int(36)));
2157 self.sm.push("");
2158 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2159 self.sm.pop(); self.sm.pop(); self.sm.push("");
2162 self.sm.push("");
2163 self.emit_op(StackOp::Drop);
2164 self.sm.pop();
2165 }
2166 "extractSigHashType" => {
2167 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2170 self.sm.push("");
2171 self.sm.push("");
2172 self.emit_op(StackOp::Push(PushValue::Int(4)));
2173 self.sm.push("");
2174 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2175 self.sm.pop();
2176 self.sm.pop();
2177 self.sm.push("");
2178 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2179 self.sm.pop();
2180 self.sm.pop();
2181 self.sm.push("");
2182 self.sm.push("");
2183 self.emit_op(StackOp::Nip);
2184 self.sm.pop();
2185 self.sm.pop();
2186 self.sm.push("");
2187 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2188 }
2189 "extractLocktime" => {
2190 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2193 self.sm.push("");
2194 self.sm.push("");
2195 self.emit_op(StackOp::Push(PushValue::Int(8)));
2196 self.sm.push("");
2197 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2198 self.sm.pop();
2199 self.sm.pop();
2200 self.sm.push("");
2201 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2202 self.sm.pop();
2203 self.sm.pop();
2204 self.sm.push("");
2205 self.sm.push("");
2206 self.emit_op(StackOp::Nip);
2207 self.sm.pop();
2208 self.sm.pop();
2209 self.sm.push("");
2210 self.emit_op(StackOp::Push(PushValue::Int(4)));
2211 self.sm.push("");
2212 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2213 self.sm.pop(); self.sm.pop(); self.sm.push("");
2216 self.sm.push("");
2217 self.emit_op(StackOp::Drop);
2218 self.sm.pop();
2219 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2220 }
2221 "extractOutputHash" | "extractOutputs" => {
2222 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2225 self.sm.push("");
2226 self.sm.push("");
2227 self.emit_op(StackOp::Push(PushValue::Int(40)));
2228 self.sm.push("");
2229 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2230 self.sm.pop();
2231 self.sm.pop();
2232 self.sm.push("");
2233 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2234 self.sm.pop();
2235 self.sm.pop();
2236 self.sm.push("");
2237 self.sm.push("");
2238 self.emit_op(StackOp::Nip);
2239 self.sm.pop();
2240 self.sm.pop();
2241 self.sm.push("");
2242 self.emit_op(StackOp::Push(PushValue::Int(32)));
2243 self.sm.push("");
2244 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2245 self.sm.pop(); self.sm.pop(); self.sm.push("");
2248 self.sm.push("");
2249 self.emit_op(StackOp::Drop);
2250 self.sm.pop();
2251 }
2252 "extractAmount" => {
2253 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2256 self.sm.push("");
2257 self.sm.push("");
2258 self.emit_op(StackOp::Push(PushValue::Int(52)));
2259 self.sm.push("");
2260 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2261 self.sm.pop();
2262 self.sm.pop();
2263 self.sm.push("");
2264 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2265 self.sm.pop();
2266 self.sm.pop();
2267 self.sm.push("");
2268 self.sm.push("");
2269 self.emit_op(StackOp::Nip);
2270 self.sm.pop();
2271 self.sm.pop();
2272 self.sm.push("");
2273 self.emit_op(StackOp::Push(PushValue::Int(8)));
2274 self.sm.push("");
2275 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2276 self.sm.pop(); self.sm.pop(); self.sm.push("");
2279 self.sm.push("");
2280 self.emit_op(StackOp::Drop);
2281 self.sm.pop();
2282 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2283 }
2284 "extractSequence" => {
2285 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2288 self.sm.push("");
2289 self.sm.push("");
2290 self.emit_op(StackOp::Push(PushValue::Int(44)));
2291 self.sm.push("");
2292 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2293 self.sm.pop();
2294 self.sm.pop();
2295 self.sm.push("");
2296 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2297 self.sm.pop();
2298 self.sm.pop();
2299 self.sm.push("");
2300 self.sm.push("");
2301 self.emit_op(StackOp::Nip);
2302 self.sm.pop();
2303 self.sm.pop();
2304 self.sm.push("");
2305 self.emit_op(StackOp::Push(PushValue::Int(4)));
2306 self.sm.push("");
2307 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2308 self.sm.pop(); self.sm.pop(); self.sm.push("");
2311 self.sm.push("");
2312 self.emit_op(StackOp::Drop);
2313 self.sm.pop();
2314 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2315 }
2316 "extractScriptCode" => {
2317 self.emit_op(StackOp::Push(PushValue::Int(104)));
2320 self.sm.push("");
2321 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2322 self.sm.pop();
2323 self.sm.push("");
2324 self.sm.push("");
2325 self.emit_op(StackOp::Nip);
2326 self.sm.pop();
2327 self.sm.pop();
2328 self.sm.push("");
2329 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2330 self.sm.push("");
2331 self.emit_op(StackOp::Push(PushValue::Int(52)));
2332 self.sm.push("");
2333 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2334 self.sm.pop();
2335 self.sm.pop();
2336 self.sm.push("");
2337 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2338 self.sm.pop();
2339 self.sm.pop();
2340 self.sm.push("");
2341 self.sm.push("");
2342 self.emit_op(StackOp::Drop);
2343 self.sm.pop();
2344 }
2345 "extractInputIndex" => {
2346 self.emit_op(StackOp::Push(PushValue::Int(100)));
2349 self.sm.push("");
2350 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2351 self.sm.pop();
2352 self.sm.push("");
2353 self.sm.push("");
2354 self.emit_op(StackOp::Nip);
2355 self.sm.pop();
2356 self.sm.pop();
2357 self.sm.push("");
2358 self.emit_op(StackOp::Push(PushValue::Int(4)));
2359 self.sm.push("");
2360 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2361 self.sm.pop(); self.sm.pop(); self.sm.push("");
2364 self.sm.push("");
2365 self.emit_op(StackOp::Drop);
2366 self.sm.pop();
2367 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2368 }
2369 _ => panic!("unknown extractor: {}", func_name),
2370 }
2371
2372 self.sm.pop();
2374 self.sm.push(binding_name);
2375 self.track_depth();
2376 }
2377
2378 fn lower_array_access(
2392 &mut self,
2393 binding_name: &str,
2394 args: &[String],
2395 binding_index: usize,
2396 last_uses: &HashMap<String, usize>,
2397 ) {
2398 assert!(args.len() >= 2, "__array_access requires 2 arguments (object, index)");
2399
2400 let obj = &args[0];
2401 let index = &args[1];
2402
2403 let obj_is_last = self.is_last_use(obj, binding_index, last_uses);
2405 self.bring_to_top(obj, obj_is_last);
2406
2407 let index_is_last = self.is_last_use(index, binding_index, last_uses);
2409 self.bring_to_top(index, index_is_last);
2410
2411 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2415 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip);
2420 self.sm.pop();
2421 let right_part = self.sm.pop();
2422 self.sm.push(&right_part);
2423
2424 self.emit_op(StackOp::Push(PushValue::Int(1)));
2426 self.sm.push("");
2427
2428 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2432 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
2437 self.sm.pop();
2438 self.sm.pop();
2439 self.sm.push("");
2440
2441 self.sm.pop();
2443 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2444
2445 self.sm.push(binding_name);
2446 self.track_depth();
2447 }
2448
2449 fn lower_reverse_bytes(
2450 &mut self,
2451 binding_name: &str,
2452 args: &[String],
2453 binding_index: usize,
2454 last_uses: &HashMap<String, usize>,
2455 ) {
2456 assert!(!args.is_empty(), "reverseBytes requires 1 argument");
2457 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
2458 self.bring_to_top(&args[0], is_last);
2459
2460 self.sm.pop();
2464
2465 self.emit_op(StackOp::Push(PushValue::Int(0)));
2467 self.emit_op(StackOp::Swap);
2468
2469 for _ in 0..520 {
2471 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2473 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2474 self.emit_op(StackOp::Nip);
2475 self.emit_op(StackOp::If {
2476 then_ops: vec![
2477 StackOp::Push(PushValue::Int(1)),
2478 StackOp::Opcode("OP_SPLIT".to_string()),
2479 StackOp::Swap,
2480 StackOp::Rot,
2481 StackOp::Opcode("OP_CAT".to_string()),
2482 StackOp::Swap,
2483 ],
2484 else_ops: vec![],
2485 });
2486 }
2487
2488 self.emit_op(StackOp::Drop);
2490
2491 self.sm.push(binding_name);
2492 self.track_depth();
2493 }
2494
2495 fn lower_substr(
2496 &mut self,
2497 binding_name: &str,
2498 args: &[String],
2499 binding_index: usize,
2500 last_uses: &HashMap<String, usize>,
2501 ) {
2502 assert!(args.len() >= 3, "substr requires 3 arguments");
2503
2504 let data = &args[0];
2505 let start = &args[1];
2506 let length = &args[2];
2507
2508 let data_is_last = self.is_last_use(data, binding_index, last_uses);
2509 self.bring_to_top(data, data_is_last);
2510
2511 let start_is_last = self.is_last_use(start, binding_index, last_uses);
2512 self.bring_to_top(start, start_is_last);
2513
2514 self.sm.pop();
2515 self.sm.pop();
2516 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2517 self.sm.push("");
2518 self.sm.push("");
2519
2520 self.emit_op(StackOp::Nip);
2521 self.sm.pop();
2522 let right_part = self.sm.pop();
2523 self.sm.push(&right_part);
2524
2525 let len_is_last = self.is_last_use(length, binding_index, last_uses);
2526 self.bring_to_top(length, len_is_last);
2527
2528 self.sm.pop();
2529 self.sm.pop();
2530 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2531 self.sm.push("");
2532 self.sm.push("");
2533
2534 self.emit_op(StackOp::Drop);
2535 self.sm.pop();
2536 self.sm.pop();
2537
2538 self.sm.push(binding_name);
2539 self.track_depth();
2540 }
2541 fn lower_verify_rabin_sig(
2542 &mut self,
2543 binding_name: &str,
2544 args: &[String],
2545 binding_index: usize,
2546 last_uses: &HashMap<String, usize>,
2547 ) {
2548 assert!(args.len() >= 4, "verifyRabinSig requires 4 arguments");
2549
2550 let msg = &args[0];
2554 let sig = &args[1];
2555 let padding = &args[2];
2556 let pub_key = &args[3];
2557
2558 let msg_is_last = self.is_last_use(msg, binding_index, last_uses);
2559 self.bring_to_top(msg, msg_is_last);
2560
2561 let sig_is_last = self.is_last_use(sig, binding_index, last_uses);
2562 self.bring_to_top(sig, sig_is_last);
2563
2564 let padding_is_last = self.is_last_use(padding, binding_index, last_uses);
2565 self.bring_to_top(padding, padding_is_last);
2566
2567 let pub_key_is_last = self.is_last_use(pub_key, binding_index, last_uses);
2568 self.bring_to_top(pub_key, pub_key_is_last);
2569
2570 self.sm.pop();
2572 self.sm.pop();
2573 self.sm.pop();
2574 self.sm.pop();
2575
2576 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()));
2581 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()));
2587 self.emit_op(StackOp::Opcode("OP_EQUAL".to_string()));
2588
2589 self.sm.push(binding_name);
2590 self.track_depth();
2591 }
2592
2593 fn lower_sign(
2596 &mut self,
2597 binding_name: &str,
2598 args: &[String],
2599 binding_index: usize,
2600 last_uses: &HashMap<String, usize>,
2601 ) {
2602 assert!(!args.is_empty(), "sign requires 1 argument");
2603 let x = &args[0];
2604
2605 let x_is_last = self.is_last_use(x, binding_index, last_uses);
2606 self.bring_to_top(x, x_is_last);
2607 self.sm.pop();
2608
2609 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2610 self.emit_op(StackOp::If {
2611 then_ops: vec![
2612 StackOp::Opcode("OP_DUP".to_string()),
2613 StackOp::Opcode("OP_ABS".to_string()),
2614 StackOp::Swap,
2615 StackOp::Opcode("OP_DIV".to_string()),
2616 ],
2617 else_ops: vec![],
2618 });
2619
2620 self.sm.push(binding_name);
2621 self.track_depth();
2622 }
2623
2624 fn lower_right(
2627 &mut self,
2628 binding_name: &str,
2629 args: &[String],
2630 binding_index: usize,
2631 last_uses: &HashMap<String, usize>,
2632 ) {
2633 assert!(args.len() >= 2, "right requires 2 arguments");
2634 let data = &args[0];
2635 let length = &args[1];
2636
2637 let data_is_last = self.is_last_use(data, binding_index, last_uses);
2638 self.bring_to_top(data, data_is_last);
2639
2640 let length_is_last = self.is_last_use(length, binding_index, last_uses);
2641 self.bring_to_top(length, length_is_last);
2642
2643 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);
2654 self.track_depth();
2655 }
2656
2657 fn emit_wots_one_chain(&mut self, chain_index: usize) {
2661 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2663 self.emit_op(StackOp::Push(PushValue::Int(15)));
2664 self.emit_op(StackOp::Swap);
2665 self.emit_op(StackOp::Opcode("OP_SUB".into()));
2666 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2670 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2672 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2677 self.emit_op(StackOp::Push(PushValue::Int(32)));
2678 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2679 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
2681 for j in 0..15usize {
2687 let adrs_bytes = vec![chain_index as u8, j as u8];
2688 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2689 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".into()));
2690 self.emit_op(StackOp::If {
2691 then_ops: vec![
2692 StackOp::Opcode("OP_1SUB".into()), ],
2694 else_ops: vec![
2695 StackOp::Swap, StackOp::Push(PushValue::Int(2)),
2697 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, ],
2705 });
2706 }
2707 self.emit_op(StackOp::Drop); self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2712 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2713 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2714 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2715
2716 self.emit_op(StackOp::Rot);
2718 self.emit_op(StackOp::Opcode("OP_ADD".into()));
2719
2720 self.emit_op(StackOp::Swap);
2722 self.emit_op(StackOp::Push(PushValue::Int(3)));
2723 self.emit_op(StackOp::Opcode("OP_ROLL".into()));
2724 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2725 }
2726
2727 fn lower_verify_wots(
2731 &mut self,
2732 binding_name: &str,
2733 args: &[String],
2734 binding_index: usize,
2735 last_uses: &HashMap<String, usize>,
2736 ) {
2737 assert!(args.len() >= 3, "verifyWOTS requires 3 arguments: msg, sig, pubkey");
2738
2739 for arg in args.iter() {
2740 let is_last = self.is_last_use(arg, binding_index, last_uses);
2741 self.bring_to_top(arg, is_last);
2742 }
2743 for _ in 0..3 { self.sm.pop(); }
2744 self.emit_op(StackOp::Push(PushValue::Int(32)));
2748 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);
2759 self.emit_op(StackOp::Push(PushValue::Int(0)));
2760 self.emit_op(StackOp::Opcode("OP_0".into()));
2761 self.emit_op(StackOp::Push(PushValue::Int(3)));
2762 self.emit_op(StackOp::Opcode("OP_ROLL".into()));
2763
2764 for byte_idx in 0..32 {
2766 if byte_idx < 31 {
2767 self.emit_op(StackOp::Push(PushValue::Int(1)));
2768 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2769 self.emit_op(StackOp::Swap);
2770 }
2771 self.emit_op(StackOp::Push(PushValue::Int(0)));
2773 self.emit_op(StackOp::Push(PushValue::Int(1)));
2774 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
2775 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2776 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
2777 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2779 self.emit_op(StackOp::Push(PushValue::Int(16)));
2780 self.emit_op(StackOp::Opcode("OP_DIV".into()));
2781 self.emit_op(StackOp::Swap);
2782 self.emit_op(StackOp::Push(PushValue::Int(16)));
2783 self.emit_op(StackOp::Opcode("OP_MOD".into()));
2784
2785 if byte_idx < 31 {
2786 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2787 self.emit_op(StackOp::Swap);
2788 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2789 } else {
2790 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2791 }
2792
2793 self.emit_wots_one_chain(byte_idx * 2); if byte_idx < 31 {
2796 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2797 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2798 self.emit_op(StackOp::Swap);
2799 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2800 } else {
2801 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2802 }
2803
2804 self.emit_wots_one_chain(byte_idx * 2 + 1); if byte_idx < 31 {
2807 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2808 }
2809 }
2810
2811 self.emit_op(StackOp::Swap);
2813 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2815 self.emit_op(StackOp::Push(PushValue::Int(16)));
2816 self.emit_op(StackOp::Opcode("OP_MOD".into()));
2817 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2818 self.emit_op(StackOp::Opcode("OP_DUP".into()));
2820 self.emit_op(StackOp::Push(PushValue::Int(16)));
2821 self.emit_op(StackOp::Opcode("OP_DIV".into()));
2822 self.emit_op(StackOp::Push(PushValue::Int(16)));
2823 self.emit_op(StackOp::Opcode("OP_MOD".into()));
2824 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2825 self.emit_op(StackOp::Push(PushValue::Int(256)));
2827 self.emit_op(StackOp::Opcode("OP_DIV".into()));
2828 self.emit_op(StackOp::Push(PushValue::Int(16)));
2829 self.emit_op(StackOp::Opcode("OP_MOD".into()));
2830 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2831
2832 for ci in 0..3 {
2834 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
2835 self.emit_op(StackOp::Push(PushValue::Int(0)));
2836 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2837 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
2838 self.emit_wots_one_chain(64 + ci);
2839 self.emit_op(StackOp::Swap);
2840 self.emit_op(StackOp::Drop);
2841 }
2842
2843 self.emit_op(StackOp::Swap);
2845 self.emit_op(StackOp::Drop);
2846 self.emit_op(StackOp::Opcode("OP_SHA256".into()));
2848 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into())); self.emit_op(StackOp::Opcode("OP_EQUAL".into()));
2850 self.emit_op(StackOp::Swap);
2852 self.emit_op(StackOp::Drop);
2853
2854 self.sm.push(binding_name);
2855 self.track_depth();
2856 }
2857
2858 fn lower_verify_slh_dsa(
2862 &mut self,
2863 binding_name: &str,
2864 param_key: &str,
2865 args: &[String],
2866 binding_index: usize,
2867 last_uses: &HashMap<String, usize>,
2868 ) {
2869 assert!(
2870 args.len() >= 3,
2871 "verifySLHDSA requires 3 arguments: msg, sig, pubkey"
2872 );
2873
2874 for arg in args.iter() {
2876 let is_last = self.is_last_use(arg, binding_index, last_uses);
2877 self.bring_to_top(arg, is_last);
2878 }
2879 for _ in 0..3 {
2880 self.sm.pop();
2881 }
2882
2883 super::slh_dsa::emit_verify_slh_dsa(&mut |op| self.ops.push(op), param_key);
2885
2886 self.sm.push(binding_name);
2887 self.track_depth();
2888 }
2889
2890 fn lower_ec_builtin(
2891 &mut self,
2892 binding_name: &str,
2893 func_name: &str,
2894 args: &[String],
2895 binding_index: usize,
2896 last_uses: &HashMap<String, usize>,
2897 ) {
2898 for arg in args.iter() {
2900 let is_last = self.is_last_use(arg, binding_index, last_uses);
2901 self.bring_to_top(arg, is_last);
2902 }
2903 for _ in args {
2904 self.sm.pop();
2905 }
2906
2907 let emit = &mut |op: StackOp| self.ops.push(op);
2908
2909 match func_name {
2910 "ecAdd" => super::ec::emit_ec_add(emit),
2911 "ecMul" => super::ec::emit_ec_mul(emit),
2912 "ecMulGen" => super::ec::emit_ec_mul_gen(emit),
2913 "ecNegate" => super::ec::emit_ec_negate(emit),
2914 "ecOnCurve" => super::ec::emit_ec_on_curve(emit),
2915 "ecModReduce" => super::ec::emit_ec_mod_reduce(emit),
2916 "ecEncodeCompressed" => super::ec::emit_ec_encode_compressed(emit),
2917 "ecMakePoint" => super::ec::emit_ec_make_point(emit),
2918 "ecPointX" => super::ec::emit_ec_point_x(emit),
2919 "ecPointY" => super::ec::emit_ec_point_y(emit),
2920 _ => panic!("unknown EC builtin: {}", func_name),
2921 }
2922
2923 self.sm.push(binding_name);
2924 self.track_depth();
2925 }
2926
2927 fn lower_safediv(
2930 &mut self,
2931 binding_name: &str,
2932 args: &[String],
2933 binding_index: usize,
2934 last_uses: &HashMap<String, usize>,
2935 ) {
2936 assert!(args.len() >= 2, "safediv requires 2 arguments");
2937
2938 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
2939 self.bring_to_top(&args[0], a_is_last);
2940
2941 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
2942 self.bring_to_top(&args[1], b_is_last);
2943
2944 self.sm.pop();
2945 self.sm.pop();
2946
2947 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2948 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string()));
2949 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
2950 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
2951
2952 self.sm.push(binding_name);
2953 self.track_depth();
2954 }
2955
2956 fn lower_safemod(
2959 &mut self,
2960 binding_name: &str,
2961 args: &[String],
2962 binding_index: usize,
2963 last_uses: &HashMap<String, usize>,
2964 ) {
2965 assert!(args.len() >= 2, "safemod requires 2 arguments");
2966
2967 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
2968 self.bring_to_top(&args[0], a_is_last);
2969
2970 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
2971 self.bring_to_top(&args[1], b_is_last);
2972
2973 self.sm.pop();
2974 self.sm.pop();
2975
2976 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
2977 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string()));
2978 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
2979 self.emit_op(StackOp::Opcode("OP_MOD".to_string()));
2980
2981 self.sm.push(binding_name);
2982 self.track_depth();
2983 }
2984
2985 fn lower_clamp(
2988 &mut self,
2989 binding_name: &str,
2990 args: &[String],
2991 binding_index: usize,
2992 last_uses: &HashMap<String, usize>,
2993 ) {
2994 assert!(args.len() >= 3, "clamp requires 3 arguments");
2995
2996 let val_is_last = self.is_last_use(&args[0], binding_index, last_uses);
2997 self.bring_to_top(&args[0], val_is_last);
2998
2999 let lo_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3000 self.bring_to_top(&args[1], lo_is_last);
3001
3002 self.sm.pop();
3003 self.sm.pop();
3004 self.emit_op(StackOp::Opcode("OP_MAX".to_string()));
3005 self.sm.push(""); let hi_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3008 self.bring_to_top(&args[2], hi_is_last);
3009
3010 self.sm.pop();
3011 self.sm.pop();
3012 self.emit_op(StackOp::Opcode("OP_MIN".to_string()));
3013
3014 self.sm.push(binding_name);
3015 self.track_depth();
3016 }
3017
3018 fn lower_pow(
3023 &mut self,
3024 binding_name: &str,
3025 args: &[String],
3026 binding_index: usize,
3027 last_uses: &HashMap<String, usize>,
3028 ) {
3029 assert!(args.len() >= 2, "pow requires 2 arguments");
3030
3031 let base_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3032 self.bring_to_top(&args[0], base_is_last);
3033
3034 let exp_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3035 self.bring_to_top(&args[1], exp_is_last);
3036
3037 self.sm.pop();
3038 self.sm.pop();
3039
3040 self.emit_op(StackOp::Swap); self.emit_op(StackOp::Push(PushValue::Int(1))); for i in 0..32 {
3045 self.emit_op(StackOp::Push(PushValue::Int(2)));
3047 self.emit_op(StackOp::Opcode("OP_PICK".to_string())); self.emit_op(StackOp::Push(PushValue::Int(i)));
3049 self.emit_op(StackOp::Opcode("OP_GREATERTHAN".to_string())); self.emit_op(StackOp::If {
3051 then_ops: vec![
3052 StackOp::Over, StackOp::Opcode("OP_MUL".to_string()), ],
3055 else_ops: vec![],
3056 });
3057 }
3058 self.emit_op(StackOp::Nip); self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3063 self.track_depth();
3064 }
3065
3066 fn lower_mul_div(
3069 &mut self,
3070 binding_name: &str,
3071 args: &[String],
3072 binding_index: usize,
3073 last_uses: &HashMap<String, usize>,
3074 ) {
3075 assert!(args.len() >= 3, "mulDiv requires 3 arguments");
3076
3077 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3078 self.bring_to_top(&args[0], a_is_last);
3079
3080 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3081 self.bring_to_top(&args[1], b_is_last);
3082
3083 self.sm.pop();
3084 self.sm.pop();
3085 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3086 self.sm.push(""); let c_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3089 self.bring_to_top(&args[2], c_is_last);
3090
3091 self.sm.pop();
3092 self.sm.pop();
3093 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3094
3095 self.sm.push(binding_name);
3096 self.track_depth();
3097 }
3098
3099 fn lower_percent_of(
3102 &mut self,
3103 binding_name: &str,
3104 args: &[String],
3105 binding_index: usize,
3106 last_uses: &HashMap<String, usize>,
3107 ) {
3108 assert!(args.len() >= 2, "percentOf requires 2 arguments");
3109
3110 let amount_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3111 self.bring_to_top(&args[0], amount_is_last);
3112
3113 let bps_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3114 self.bring_to_top(&args[1], bps_is_last);
3115
3116 self.sm.pop();
3117 self.sm.pop();
3118
3119 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3120 self.emit_op(StackOp::Push(PushValue::Int(10000)));
3121 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3122
3123 self.sm.push(binding_name);
3124 self.track_depth();
3125 }
3126
3127 fn lower_sqrt(
3131 &mut self,
3132 binding_name: &str,
3133 args: &[String],
3134 binding_index: usize,
3135 last_uses: &HashMap<String, usize>,
3136 ) {
3137 assert!(!args.is_empty(), "sqrt requires 1 argument");
3138
3139 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3140 self.bring_to_top(&args[0], n_is_last);
3141
3142 self.sm.pop();
3143
3144 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3147 let mut newton_ops = Vec::new();
3151 newton_ops.push(StackOp::Opcode("OP_DUP".to_string()));
3154 for _ in 0..16 {
3158 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())); }
3166
3167 newton_ops.push(StackOp::Opcode("OP_NIP".to_string()));
3170
3171 self.emit_op(StackOp::If {
3172 then_ops: newton_ops,
3173 else_ops: vec![], });
3175
3176 self.sm.push(binding_name);
3177 self.track_depth();
3178 }
3179
3180 fn lower_gcd(
3183 &mut self,
3184 binding_name: &str,
3185 args: &[String],
3186 binding_index: usize,
3187 last_uses: &HashMap<String, usize>,
3188 ) {
3189 assert!(args.len() >= 2, "gcd requires 2 arguments");
3190
3191 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3192 self.bring_to_top(&args[0], a_is_last);
3193
3194 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3195 self.bring_to_top(&args[1], b_is_last);
3196
3197 self.sm.pop();
3198 self.sm.pop();
3199
3200 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3203 self.emit_op(StackOp::Swap);
3204 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3205 self.emit_op(StackOp::Swap);
3206 for _ in 0..256 {
3210 self.emit_op(StackOp::Opcode("OP_DUP".to_string())); self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string())); self.emit_op(StackOp::If {
3216 then_ops: vec![
3217 StackOp::Opcode("OP_TUCK".to_string()), StackOp::Opcode("OP_MOD".to_string()), ],
3222 else_ops: vec![
3223 ],
3225 });
3226 }
3227
3228 self.emit_op(StackOp::Drop);
3231
3232 self.sm.push(binding_name);
3233 self.track_depth();
3234 }
3235
3236 fn lower_divmod(
3239 &mut self,
3240 binding_name: &str,
3241 args: &[String],
3242 binding_index: usize,
3243 last_uses: &HashMap<String, usize>,
3244 ) {
3245 assert!(args.len() >= 2, "divmod requires 2 arguments");
3246
3247 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3248 self.bring_to_top(&args[0], a_is_last);
3249
3250 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3251 self.bring_to_top(&args[1], b_is_last);
3252
3253 self.sm.pop();
3254 self.sm.pop();
3255
3256 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);
3266
3267 self.sm.push(binding_name);
3268 self.track_depth();
3269 }
3270
3271 fn lower_log2(
3281 &mut self,
3282 binding_name: &str,
3283 args: &[String],
3284 binding_index: usize,
3285 last_uses: &HashMap<String, usize>,
3286 ) {
3287 assert!(!args.is_empty(), "log2 requires 1 argument");
3288
3289 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3290 self.bring_to_top(&args[0], n_is_last);
3291
3292 self.sm.pop();
3293
3294 self.emit_op(StackOp::Push(PushValue::Int(0))); const LOG2_ITERATIONS: usize = 64;
3300 for _ in 0..LOG2_ITERATIONS {
3301 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 {
3307 then_ops: vec![
3308 StackOp::Push(PushValue::Int(2)), StackOp::Opcode("OP_DIV".to_string()), StackOp::Swap, StackOp::Opcode("OP_1ADD".to_string()), StackOp::Swap, ],
3314 else_ops: vec![],
3315 });
3316 self.emit_op(StackOp::Swap); }
3320 self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3325 self.track_depth();
3326 }
3327}
3328
3329pub fn lower_to_stack(program: &ANFProgram) -> Result<Vec<StackMethod>, String> {
3337 let mut private_methods: HashMap<String, ANFMethod> = HashMap::new();
3339 for method in &program.methods {
3340 if !method.is_public && method.name != "constructor" {
3341 private_methods.insert(method.name.clone(), method.clone());
3342 }
3343 }
3344
3345 let mut methods = Vec::new();
3346
3347 for method in &program.methods {
3348 if method.name == "constructor" || (!method.is_public && method.name != "constructor") {
3350 continue;
3351 }
3352 let sm = lower_method_with_private_methods(method, &program.properties, &private_methods)?;
3353 methods.push(sm);
3354 }
3355
3356 Ok(methods)
3357}
3358
3359fn method_uses_check_preimage(bindings: &[ANFBinding]) -> bool {
3363 bindings.iter().any(|b| matches!(&b.value, ANFValue::CheckPreimage { .. }))
3364}
3365
3366fn lower_method_with_private_methods(
3367 method: &ANFMethod,
3368 properties: &[ANFProperty],
3369 private_methods: &HashMap<String, ANFMethod>,
3370) -> Result<StackMethod, String> {
3371 let mut param_names: Vec<String> = method.params.iter().map(|p| p.name.clone()).collect();
3372
3373 if method_uses_check_preimage(&method.body) {
3378 param_names.insert(0, "_opPushTxSig".to_string());
3379 }
3380
3381 let mut ctx = LoweringContext::new(¶m_names, properties);
3382 ctx.private_methods = private_methods.clone();
3383 ctx.lower_bindings(&method.body, method.is_public);
3386
3387 let has_deserialize_state = method.body.iter().any(|b| matches!(&b.value, ANFValue::DeserializeState { .. }));
3389 if method.is_public && has_deserialize_state && ctx.sm.depth() > 1 {
3390 let excess = ctx.sm.depth() - 1;
3391 for _ in 0..excess {
3392 ctx.emit_op(StackOp::Nip);
3393 ctx.sm.remove_at_depth(1);
3394 }
3395 }
3396
3397 if ctx.max_depth > MAX_STACK_DEPTH {
3398 return Err(format!(
3399 "method '{}' exceeds maximum stack depth of {} (actual: {}). Simplify the contract logic.",
3400 method.name, MAX_STACK_DEPTH, ctx.max_depth
3401 ));
3402 }
3403
3404 Ok(StackMethod {
3405 name: method.name.clone(),
3406 ops: ctx.ops,
3407 max_stack_depth: ctx.max_depth,
3408 })
3409}
3410
3411fn hex_to_bytes(hex_str: &str) -> Vec<u8> {
3416 if hex_str.is_empty() {
3417 return Vec::new();
3418 }
3419 assert!(
3420 hex_str.len() % 2 == 0,
3421 "invalid hex string length: {}",
3422 hex_str.len()
3423 );
3424 (0..hex_str.len())
3425 .step_by(2)
3426 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap_or(0))
3427 .collect()
3428}
3429
3430#[cfg(test)]
3435mod tests {
3436 use super::*;
3437 use crate::ir::{ANFBinding, ANFMethod, ANFParam, ANFProgram, ANFProperty, ANFValue};
3438
3439 fn p2pkh_program() -> ANFProgram {
3441 ANFProgram {
3442 contract_name: "P2PKH".to_string(),
3443 properties: vec![ANFProperty {
3444 name: "pubKeyHash".to_string(),
3445 prop_type: "Addr".to_string(),
3446 readonly: true,
3447 initial_value: None,
3448 }],
3449 methods: vec![ANFMethod {
3450 name: "unlock".to_string(),
3451 params: vec![
3452 ANFParam {
3453 name: "sig".to_string(),
3454 param_type: "Sig".to_string(),
3455 },
3456 ANFParam {
3457 name: "pubKey".to_string(),
3458 param_type: "PubKey".to_string(),
3459 },
3460 ],
3461 body: vec![
3462 ANFBinding {
3463 name: "t0".to_string(),
3464 value: ANFValue::LoadParam {
3465 name: "sig".to_string(),
3466 },
3467 },
3468 ANFBinding {
3469 name: "t1".to_string(),
3470 value: ANFValue::LoadParam {
3471 name: "pubKey".to_string(),
3472 },
3473 },
3474 ANFBinding {
3475 name: "t2".to_string(),
3476 value: ANFValue::LoadProp {
3477 name: "pubKeyHash".to_string(),
3478 },
3479 },
3480 ANFBinding {
3481 name: "t3".to_string(),
3482 value: ANFValue::Call {
3483 func: "hash160".to_string(),
3484 args: vec!["t1".to_string()],
3485 },
3486 },
3487 ANFBinding {
3488 name: "t4".to_string(),
3489 value: ANFValue::BinOp {
3490 op: "===".to_string(),
3491 left: "t3".to_string(),
3492 right: "t2".to_string(),
3493 result_type: None,
3494 },
3495 },
3496 ANFBinding {
3497 name: "t5".to_string(),
3498 value: ANFValue::Assert {
3499 value: "t4".to_string(),
3500 },
3501 },
3502 ANFBinding {
3503 name: "t6".to_string(),
3504 value: ANFValue::Call {
3505 func: "checkSig".to_string(),
3506 args: vec!["t0".to_string(), "t1".to_string()],
3507 },
3508 },
3509 ANFBinding {
3510 name: "t7".to_string(),
3511 value: ANFValue::Assert {
3512 value: "t6".to_string(),
3513 },
3514 },
3515 ],
3516 is_public: true,
3517 }],
3518 }
3519 }
3520
3521 #[test]
3522 fn test_p2pkh_stack_lowering_produces_placeholder_ops() {
3523 let program = p2pkh_program();
3524 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3525 assert_eq!(methods.len(), 1);
3526 assert_eq!(methods[0].name, "unlock");
3527
3528 let has_placeholder = methods[0].ops.iter().any(|op| {
3530 matches!(op, StackOp::Placeholder { .. })
3531 });
3532 assert!(
3533 has_placeholder,
3534 "P2PKH should have Placeholder ops for constructor params, ops: {:?}",
3535 methods[0].ops
3536 );
3537 }
3538
3539 #[test]
3540 fn test_placeholder_has_correct_param_index() {
3541 let program = p2pkh_program();
3542 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3543
3544 let placeholders: Vec<&StackOp> = methods[0]
3546 .ops
3547 .iter()
3548 .filter(|op| matches!(op, StackOp::Placeholder { .. }))
3549 .collect();
3550
3551 assert!(
3552 !placeholders.is_empty(),
3553 "should have at least one Placeholder"
3554 );
3555
3556 if let StackOp::Placeholder {
3558 param_index,
3559 param_name,
3560 } = placeholders[0]
3561 {
3562 assert_eq!(*param_index, 0);
3563 assert_eq!(param_name, "pubKeyHash");
3564 } else {
3565 panic!("expected Placeholder op");
3566 }
3567 }
3568
3569 #[test]
3570 fn test_with_initial_values_no_placeholder_ops() {
3571 let mut program = p2pkh_program();
3572 program.properties[0].initial_value =
3574 Some(serde_json::Value::String("aabbccdd".to_string()));
3575
3576 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3577 let has_placeholder = methods[0].ops.iter().any(|op| {
3578 matches!(op, StackOp::Placeholder { .. })
3579 });
3580 assert!(
3581 !has_placeholder,
3582 "with initial values, there should be no Placeholder ops"
3583 );
3584 }
3585
3586 #[test]
3587 fn test_stack_lowering_produces_standard_opcodes() {
3588 let program = p2pkh_program();
3589 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3590
3591 let opcodes: Vec<&str> = methods[0]
3593 .ops
3594 .iter()
3595 .filter_map(|op| match op {
3596 StackOp::Opcode(code) => Some(code.as_str()),
3597 _ => None,
3598 })
3599 .collect();
3600
3601 assert!(
3603 opcodes.contains(&"OP_HASH160"),
3604 "expected OP_HASH160 in opcodes: {:?}",
3605 opcodes
3606 );
3607 assert!(
3608 opcodes.contains(&"OP_CHECKSIG"),
3609 "expected OP_CHECKSIG in opcodes: {:?}",
3610 opcodes
3611 );
3612 }
3613
3614 #[test]
3615 fn test_max_stack_depth_is_tracked() {
3616 let program = p2pkh_program();
3617 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3618 assert!(
3619 methods[0].max_stack_depth > 0,
3620 "max_stack_depth should be > 0"
3621 );
3622 assert!(
3624 methods[0].max_stack_depth <= 10,
3625 "max_stack_depth should be reasonable for P2PKH, got: {}",
3626 methods[0].max_stack_depth
3627 );
3628 }
3629
3630 fn collect_all_opcodes(ops: &[StackOp]) -> Vec<String> {
3635 let mut result = Vec::new();
3636 for op in ops {
3637 match op {
3638 StackOp::Opcode(code) => result.push(code.clone()),
3639 StackOp::If { then_ops, else_ops } => {
3640 result.push("OP_IF".to_string());
3641 result.extend(collect_all_opcodes(then_ops));
3642 result.push("OP_ELSE".to_string());
3643 result.extend(collect_all_opcodes(else_ops));
3644 result.push("OP_ENDIF".to_string());
3645 }
3646 StackOp::Push(PushValue::Int(n)) => {
3647 result.push(format!("PUSH({})", n));
3648 }
3649 StackOp::Drop => result.push("OP_DROP".to_string()),
3650 StackOp::Swap => result.push("OP_SWAP".to_string()),
3651 StackOp::Dup => result.push("OP_DUP".to_string()),
3652 StackOp::Over => result.push("OP_OVER".to_string()),
3653 StackOp::Rot => result.push("OP_ROT".to_string()),
3654 StackOp::Nip => result.push("OP_NIP".to_string()),
3655 _ => {}
3656 }
3657 }
3658 result
3659 }
3660
3661 fn collect_opcodes_in_if_branches(ops: &[StackOp]) -> (Vec<String>, Vec<String>) {
3662 for op in ops {
3663 if let StackOp::If { then_ops, else_ops } = op {
3664 return (collect_all_opcodes(then_ops), collect_all_opcodes(else_ops));
3665 }
3666 }
3667 (vec![], vec![])
3668 }
3669
3670 #[test]
3675 fn test_extract_output_hash_uses_offset_40() {
3676 let program = ANFProgram {
3678 contract_name: "TestExtract".to_string(),
3679 properties: vec![ANFProperty {
3680 name: "val".to_string(),
3681 prop_type: "bigint".to_string(),
3682 readonly: false,
3683 initial_value: Some(serde_json::Value::Number(serde_json::Number::from(0))),
3684 }],
3685 methods: vec![ANFMethod {
3686 name: "check".to_string(),
3687 params: vec![
3688 ANFParam { name: "preimage".to_string(), param_type: "SigHashPreimage".to_string() },
3689 ],
3690 body: vec![
3691 ANFBinding {
3692 name: "t0".to_string(),
3693 value: ANFValue::LoadParam { name: "preimage".to_string() },
3694 },
3695 ANFBinding {
3696 name: "t1".to_string(),
3697 value: ANFValue::Call {
3698 func: "extractOutputHash".to_string(),
3699 args: vec!["t0".to_string()],
3700 },
3701 },
3702 ANFBinding {
3703 name: "t2".to_string(),
3704 value: ANFValue::LoadConst { value: serde_json::Value::Bool(true) },
3705 },
3706 ANFBinding {
3707 name: "t3".to_string(),
3708 value: ANFValue::Assert { value: "t2".to_string() },
3709 },
3710 ],
3711 is_public: true,
3712 }],
3713 };
3714
3715 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3716 let opcodes = collect_all_opcodes(&methods[0].ops);
3717
3718 assert!(
3720 opcodes.contains(&"PUSH(40)".to_string()),
3721 "extractOutputHash should use offset 40 (BIP-143 hashOutputs starts at size-40), ops: {:?}",
3722 opcodes
3723 );
3724 assert!(
3725 !opcodes.contains(&"PUSH(44)".to_string()),
3726 "extractOutputHash should NOT use offset 44, ops: {:?}",
3727 opcodes
3728 );
3729 }
3730
3731 #[test]
3736 fn test_terminal_if_propagates_terminal_assert() {
3737 let program = ANFProgram {
3740 contract_name: "TerminalIf".to_string(),
3741 properties: vec![],
3742 methods: vec![ANFMethod {
3743 name: "check".to_string(),
3744 params: vec![
3745 ANFParam { name: "mode".to_string(), param_type: "boolean".to_string() },
3746 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
3747 ],
3748 body: vec![
3749 ANFBinding {
3750 name: "t0".to_string(),
3751 value: ANFValue::LoadParam { name: "mode".to_string() },
3752 },
3753 ANFBinding {
3754 name: "t1".to_string(),
3755 value: ANFValue::LoadParam { name: "x".to_string() },
3756 },
3757 ANFBinding {
3758 name: "t2".to_string(),
3759 value: ANFValue::If {
3760 cond: "t0".to_string(),
3761 then: vec![
3762 ANFBinding {
3763 name: "t3".to_string(),
3764 value: ANFValue::LoadConst {
3765 value: serde_json::Value::Number(serde_json::Number::from(10)),
3766 },
3767 },
3768 ANFBinding {
3769 name: "t4".to_string(),
3770 value: ANFValue::BinOp {
3771 op: ">".to_string(),
3772 left: "t1".to_string(),
3773 right: "t3".to_string(),
3774 result_type: None,
3775 },
3776 },
3777 ANFBinding {
3778 name: "t5".to_string(),
3779 value: ANFValue::Assert { value: "t4".to_string() },
3780 },
3781 ],
3782 else_branch: vec![
3783 ANFBinding {
3784 name: "t6".to_string(),
3785 value: ANFValue::LoadConst {
3786 value: serde_json::Value::Number(serde_json::Number::from(5)),
3787 },
3788 },
3789 ANFBinding {
3790 name: "t7".to_string(),
3791 value: ANFValue::BinOp {
3792 op: ">".to_string(),
3793 left: "t1".to_string(),
3794 right: "t6".to_string(),
3795 result_type: None,
3796 },
3797 },
3798 ANFBinding {
3799 name: "t8".to_string(),
3800 value: ANFValue::Assert { value: "t7".to_string() },
3801 },
3802 ],
3803 },
3804 },
3805 ],
3806 is_public: true,
3807 }],
3808 };
3809
3810 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3811
3812 let (then_opcodes, else_opcodes) = collect_opcodes_in_if_branches(&methods[0].ops);
3814
3815 assert!(
3817 !then_opcodes.contains(&"OP_VERIFY".to_string()),
3818 "then branch should not contain OP_VERIFY (terminal assert), got: {:?}",
3819 then_opcodes
3820 );
3821 assert!(
3822 !else_opcodes.contains(&"OP_VERIFY".to_string()),
3823 "else branch should not contain OP_VERIFY (terminal assert), got: {:?}",
3824 else_opcodes
3825 );
3826 }
3827
3828 #[test]
3833 fn test_unpack_emits_bin2num() {
3834 let program = ANFProgram {
3835 contract_name: "TestUnpack".to_string(),
3836 properties: vec![],
3837 methods: vec![ANFMethod {
3838 name: "check".to_string(),
3839 params: vec![
3840 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
3841 ],
3842 body: vec![
3843 ANFBinding {
3844 name: "t0".to_string(),
3845 value: ANFValue::LoadParam { name: "data".to_string() },
3846 },
3847 ANFBinding {
3848 name: "t1".to_string(),
3849 value: ANFValue::Call {
3850 func: "unpack".to_string(),
3851 args: vec!["t0".to_string()],
3852 },
3853 },
3854 ANFBinding {
3855 name: "t2".to_string(),
3856 value: ANFValue::LoadConst {
3857 value: serde_json::Value::Number(serde_json::Number::from(42)),
3858 },
3859 },
3860 ANFBinding {
3861 name: "t3".to_string(),
3862 value: ANFValue::BinOp {
3863 op: "===".to_string(),
3864 left: "t1".to_string(),
3865 right: "t2".to_string(),
3866 result_type: None,
3867 },
3868 },
3869 ANFBinding {
3870 name: "t4".to_string(),
3871 value: ANFValue::Assert { value: "t3".to_string() },
3872 },
3873 ],
3874 is_public: true,
3875 }],
3876 };
3877
3878 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3879 let opcodes = collect_all_opcodes(&methods[0].ops);
3880 assert!(
3881 opcodes.contains(&"OP_BIN2NUM".to_string()),
3882 "unpack should emit OP_BIN2NUM, got: {:?}",
3883 opcodes
3884 );
3885 }
3886
3887 #[test]
3888 fn test_pack_is_noop() {
3889 let program = ANFProgram {
3890 contract_name: "TestPack".to_string(),
3891 properties: vec![],
3892 methods: vec![ANFMethod {
3893 name: "check".to_string(),
3894 params: vec![
3895 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
3896 ],
3897 body: vec![
3898 ANFBinding {
3899 name: "t0".to_string(),
3900 value: ANFValue::LoadParam { name: "x".to_string() },
3901 },
3902 ANFBinding {
3903 name: "t1".to_string(),
3904 value: ANFValue::Call {
3905 func: "pack".to_string(),
3906 args: vec!["t0".to_string()],
3907 },
3908 },
3909 ANFBinding {
3910 name: "t2".to_string(),
3911 value: ANFValue::LoadConst {
3912 value: serde_json::Value::Bool(true),
3913 },
3914 },
3915 ANFBinding {
3916 name: "t3".to_string(),
3917 value: ANFValue::Assert { value: "t2".to_string() },
3918 },
3919 ],
3920 is_public: true,
3921 }],
3922 };
3923
3924 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3925 let opcodes = collect_all_opcodes(&methods[0].ops);
3926 assert!(
3928 !opcodes.contains(&"OP_BIN2NUM".to_string()),
3929 "pack should not emit OP_BIN2NUM, got: {:?}",
3930 opcodes
3931 );
3932 assert!(
3933 !opcodes.contains(&"OP_NUM2BIN".to_string()),
3934 "pack should not emit OP_NUM2BIN, got: {:?}",
3935 opcodes
3936 );
3937 }
3938
3939 #[test]
3940 fn test_to_byte_string_is_noop() {
3941 let program = ANFProgram {
3942 contract_name: "TestToByteString".to_string(),
3943 properties: vec![],
3944 methods: vec![ANFMethod {
3945 name: "check".to_string(),
3946 params: vec![
3947 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
3948 ],
3949 body: vec![
3950 ANFBinding {
3951 name: "t0".to_string(),
3952 value: ANFValue::LoadParam { name: "x".to_string() },
3953 },
3954 ANFBinding {
3955 name: "t1".to_string(),
3956 value: ANFValue::Call {
3957 func: "toByteString".to_string(),
3958 args: vec!["t0".to_string()],
3959 },
3960 },
3961 ANFBinding {
3962 name: "t2".to_string(),
3963 value: ANFValue::LoadConst {
3964 value: serde_json::Value::Bool(true),
3965 },
3966 },
3967 ANFBinding {
3968 name: "t3".to_string(),
3969 value: ANFValue::Assert { value: "t2".to_string() },
3970 },
3971 ],
3972 is_public: true,
3973 }],
3974 };
3975
3976 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
3977 let opcodes = collect_all_opcodes(&methods[0].ops);
3978 assert!(
3980 !opcodes.contains(&"OP_BIN2NUM".to_string()),
3981 "toByteString should not emit OP_BIN2NUM, got: {:?}",
3982 opcodes
3983 );
3984 }
3985
3986 #[test]
3991 fn test_sqrt_has_zero_guard() {
3992 let program = ANFProgram {
3993 contract_name: "TestSqrt".to_string(),
3994 properties: vec![],
3995 methods: vec![ANFMethod {
3996 name: "check".to_string(),
3997 params: vec![
3998 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
3999 ],
4000 body: vec![
4001 ANFBinding {
4002 name: "t0".to_string(),
4003 value: ANFValue::LoadParam { name: "n".to_string() },
4004 },
4005 ANFBinding {
4006 name: "t1".to_string(),
4007 value: ANFValue::Call {
4008 func: "sqrt".to_string(),
4009 args: vec!["t0".to_string()],
4010 },
4011 },
4012 ANFBinding {
4013 name: "t2".to_string(),
4014 value: ANFValue::LoadConst {
4015 value: serde_json::Value::Number(serde_json::Number::from(0)),
4016 },
4017 },
4018 ANFBinding {
4019 name: "t3".to_string(),
4020 value: ANFValue::BinOp {
4021 op: ">=".to_string(),
4022 left: "t1".to_string(),
4023 right: "t2".to_string(),
4024 result_type: None,
4025 },
4026 },
4027 ANFBinding {
4028 name: "t4".to_string(),
4029 value: ANFValue::Assert { value: "t3".to_string() },
4030 },
4031 ],
4032 is_public: true,
4033 }],
4034 };
4035
4036 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4037 let opcodes = collect_all_opcodes(&methods[0].ops);
4038
4039 let dup_idx = opcodes.iter().position(|o| o == "OP_DUP");
4042 let if_idx = opcodes.iter().position(|o| o == "OP_IF");
4043
4044 assert!(
4045 dup_idx.is_some() && if_idx.is_some(),
4046 "sqrt should have OP_DUP and OP_IF for zero guard, got: {:?}",
4047 opcodes
4048 );
4049 assert!(
4050 dup_idx.unwrap() < if_idx.unwrap(),
4051 "OP_DUP should come before OP_IF in sqrt zero guard, got: {:?}",
4052 opcodes
4053 );
4054 }
4055
4056 #[test]
4061 fn test_loop_cleans_up_unused_iter_var() {
4062 let program = ANFProgram {
4066 contract_name: "TestLoopCleanup".to_string(),
4067 properties: vec![],
4068 methods: vec![ANFMethod {
4069 name: "check".to_string(),
4070 params: vec![
4071 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4072 ],
4073 body: vec![
4074 ANFBinding {
4075 name: "t0".to_string(),
4076 value: ANFValue::LoadParam { name: "x".to_string() },
4077 },
4078 ANFBinding {
4079 name: "t_loop".to_string(),
4080 value: ANFValue::Loop {
4081 count: 3,
4082 body: vec![
4083 ANFBinding {
4085 name: "t1".to_string(),
4086 value: ANFValue::LoadParam { name: "x".to_string() },
4087 },
4088 ANFBinding {
4089 name: "t2".to_string(),
4090 value: ANFValue::Assert { value: "t1".to_string() },
4091 },
4092 ],
4093 iter_var: "__i".to_string(),
4094 },
4095 },
4096 ANFBinding {
4097 name: "t_final".to_string(),
4098 value: ANFValue::LoadConst {
4099 value: serde_json::Value::Bool(true),
4100 },
4101 },
4102 ANFBinding {
4103 name: "t_assert".to_string(),
4104 value: ANFValue::Assert { value: "t_final".to_string() },
4105 },
4106 ],
4107 is_public: true,
4108 }],
4109 };
4110
4111 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4112 let opcodes = collect_all_opcodes(&methods[0].ops);
4113
4114 let drop_count = opcodes.iter().filter(|o| o.as_str() == "OP_DROP").count();
4118 assert!(
4119 drop_count >= 3,
4120 "unused iter var should be dropped after each iteration; expected >= 3 OP_DROPs, got {}: {:?}",
4121 drop_count,
4122 opcodes
4123 );
4124 }
4125
4126 #[test]
4131 fn test_push_value_int_large_values() {
4132 let large_val: i128 = (i64::MAX as i128) + 1;
4134 let push = PushValue::Int(large_val);
4135 if let PushValue::Int(v) = push {
4136 assert_eq!(v, large_val, "PushValue::Int should store values > i64::MAX without truncation");
4137 } else {
4138 panic!("expected PushValue::Int");
4139 }
4140
4141 let neg_val: i128 = (i64::MIN as i128) - 1;
4143 let push_neg = PushValue::Int(neg_val);
4144 if let PushValue::Int(v) = push_neg {
4145 assert_eq!(v, neg_val, "PushValue::Int should store values < i64::MIN without truncation");
4146 } else {
4147 panic!("expected PushValue::Int");
4148 }
4149 }
4150
4151 #[test]
4152 fn test_push_value_int_encodes_large_number() {
4153 use crate::codegen::emit::encode_push_int;
4155
4156 let large_val: i128 = 1i128 << 100;
4157 let (hex, _asm) = encode_push_int(large_val);
4158 assert!(!hex.is_empty(), "encoding of 2^100 should produce non-empty hex");
4160
4161 assert!(
4165 hex.len() >= 26,
4166 "2^100 should need at least 13 bytes of push data, got hex length {}: {}",
4167 hex.len(),
4168 hex
4169 );
4170 }
4171
4172 #[test]
4177 fn test_log2_uses_bit_scanning_not_byte_approx() {
4178 let program = ANFProgram {
4179 contract_name: "TestLog2".to_string(),
4180 properties: vec![],
4181 methods: vec![ANFMethod {
4182 name: "check".to_string(),
4183 params: vec![
4184 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4185 ],
4186 body: vec![
4187 ANFBinding {
4188 name: "t0".to_string(),
4189 value: ANFValue::LoadParam { name: "n".to_string() },
4190 },
4191 ANFBinding {
4192 name: "t1".to_string(),
4193 value: ANFValue::Call {
4194 func: "log2".to_string(),
4195 args: vec!["t0".to_string()],
4196 },
4197 },
4198 ANFBinding {
4199 name: "t2".to_string(),
4200 value: ANFValue::LoadConst {
4201 value: serde_json::Value::Number(serde_json::Number::from(0)),
4202 },
4203 },
4204 ANFBinding {
4205 name: "t3".to_string(),
4206 value: ANFValue::BinOp {
4207 op: ">=".to_string(),
4208 left: "t1".to_string(),
4209 right: "t2".to_string(),
4210 result_type: None,
4211 },
4212 },
4213 ANFBinding {
4214 name: "t4".to_string(),
4215 value: ANFValue::Assert { value: "t3".to_string() },
4216 },
4217 ],
4218 is_public: true,
4219 }],
4220 };
4221
4222 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4223 let opcodes = collect_all_opcodes(&methods[0].ops);
4224
4225 assert!(
4227 opcodes.contains(&"OP_DIV".to_string()),
4228 "log2 should use OP_DIV (bit-scanning), got: {:?}",
4229 opcodes
4230 );
4231 assert!(
4232 opcodes.contains(&"OP_GREATERTHAN".to_string()),
4233 "log2 should use OP_GREATERTHAN (bit-scanning), got: {:?}",
4234 opcodes
4235 );
4236
4237 assert!(
4239 !opcodes.contains(&"OP_SIZE".to_string()),
4240 "log2 should NOT use OP_SIZE (old byte approximation), got: {:?}",
4241 opcodes
4242 );
4243 assert!(
4244 !opcodes.contains(&"OP_MUL".to_string()),
4245 "log2 should NOT use OP_MUL (old byte approximation), got: {:?}",
4246 opcodes
4247 );
4248
4249 assert!(
4251 opcodes.contains(&"OP_1ADD".to_string()),
4252 "log2 should use OP_1ADD (counter increment), got: {:?}",
4253 opcodes
4254 );
4255 }
4256
4257 #[test]
4262 fn test_reverse_bytes_uses_split_cat_not_op_reverse() {
4263 let program = ANFProgram {
4264 contract_name: "TestReverse".to_string(),
4265 properties: vec![],
4266 methods: vec![ANFMethod {
4267 name: "check".to_string(),
4268 params: vec![
4269 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
4270 ],
4271 body: vec![
4272 ANFBinding {
4273 name: "t0".to_string(),
4274 value: ANFValue::LoadParam { name: "data".to_string() },
4275 },
4276 ANFBinding {
4277 name: "t1".to_string(),
4278 value: ANFValue::Call {
4279 func: "reverseBytes".to_string(),
4280 args: vec!["t0".to_string()],
4281 },
4282 },
4283 ANFBinding {
4284 name: "t2".to_string(),
4285 value: ANFValue::LoadConst {
4286 value: serde_json::Value::Bool(true),
4287 },
4288 },
4289 ANFBinding {
4290 name: "t3".to_string(),
4291 value: ANFValue::Assert { value: "t2".to_string() },
4292 },
4293 ],
4294 is_public: true,
4295 }],
4296 };
4297
4298 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4299 let opcodes = collect_all_opcodes(&methods[0].ops);
4300
4301 assert!(
4303 !opcodes.contains(&"OP_REVERSE".to_string()),
4304 "reverseBytes must NOT emit OP_REVERSE (does not exist), got: {:?}",
4305 opcodes
4306 );
4307
4308 assert!(
4310 opcodes.contains(&"OP_SPLIT".to_string()),
4311 "reverseBytes should emit OP_SPLIT for byte peeling, got: {:?}",
4312 opcodes
4313 );
4314 assert!(
4315 opcodes.contains(&"OP_CAT".to_string()),
4316 "reverseBytes should emit OP_CAT for reassembly, got: {:?}",
4317 opcodes
4318 );
4319
4320 assert!(
4322 opcodes.contains(&"OP_SIZE".to_string()),
4323 "reverseBytes should emit OP_SIZE for length check, got: {:?}",
4324 opcodes
4325 );
4326 }
4327}