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 PushCodeSepIndex,
52}
53
54#[derive(Debug, Clone)]
56pub enum PushValue {
57 Bool(bool),
58 Int(i128),
59 Bytes(Vec<u8>),
60}
61
62#[derive(Debug, Clone)]
64pub struct StackMethod {
65 pub name: String,
66 pub ops: Vec<StackOp>,
67 pub max_stack_depth: usize,
68 pub source_locs: Vec<Option<crate::ir::SourceLocation>>,
71}
72
73fn is_ec_builtin(name: &str) -> bool {
78 matches!(
79 name,
80 "ecAdd"
81 | "ecMul"
82 | "ecMulGen"
83 | "ecNegate"
84 | "ecOnCurve"
85 | "ecModReduce"
86 | "ecEncodeCompressed"
87 | "ecMakePoint"
88 | "ecPointX"
89 | "ecPointY"
90 )
91}
92
93fn builtin_opcodes(name: &str) -> Option<Vec<&'static str>> {
94 match name {
95 "sha256" => Some(vec!["OP_SHA256"]),
96 "ripemd160" => Some(vec!["OP_RIPEMD160"]),
97 "hash160" => Some(vec!["OP_HASH160"]),
98 "hash256" => Some(vec!["OP_HASH256"]),
99 "checkSig" => Some(vec!["OP_CHECKSIG"]),
100 "checkMultiSig" => Some(vec!["OP_CHECKMULTISIG"]),
101 "len" => Some(vec!["OP_SIZE"]),
102 "cat" => Some(vec!["OP_CAT"]),
103 "num2bin" => Some(vec!["OP_NUM2BIN"]),
104 "bin2num" => Some(vec!["OP_BIN2NUM"]),
105 "abs" => Some(vec!["OP_ABS"]),
106 "min" => Some(vec!["OP_MIN"]),
107 "max" => Some(vec!["OP_MAX"]),
108 "within" => Some(vec!["OP_WITHIN"]),
109 "split" => Some(vec!["OP_SPLIT"]),
110 "left" => Some(vec!["OP_SPLIT", "OP_DROP"]),
111 "int2str" => Some(vec!["OP_NUM2BIN"]),
112 "bool" => Some(vec!["OP_0NOTEQUAL"]),
113 "unpack" => Some(vec!["OP_BIN2NUM"]),
114 _ => None,
115 }
116}
117
118fn binop_opcodes(op: &str) -> Option<Vec<&'static str>> {
119 match op {
120 "+" => Some(vec!["OP_ADD"]),
121 "-" => Some(vec!["OP_SUB"]),
122 "*" => Some(vec!["OP_MUL"]),
123 "/" => Some(vec!["OP_DIV"]),
124 "%" => Some(vec!["OP_MOD"]),
125 "===" => Some(vec!["OP_NUMEQUAL"]),
126 "!==" => Some(vec!["OP_NUMEQUAL", "OP_NOT"]),
127 "<" => Some(vec!["OP_LESSTHAN"]),
128 ">" => Some(vec!["OP_GREATERTHAN"]),
129 "<=" => Some(vec!["OP_LESSTHANOREQUAL"]),
130 ">=" => Some(vec!["OP_GREATERTHANOREQUAL"]),
131 "&&" => Some(vec!["OP_BOOLAND"]),
132 "||" => Some(vec!["OP_BOOLOR"]),
133 "&" => Some(vec!["OP_AND"]),
134 "|" => Some(vec!["OP_OR"]),
135 "^" => Some(vec!["OP_XOR"]),
136 "<<" => Some(vec!["OP_LSHIFT"]),
137 ">>" => Some(vec!["OP_RSHIFT"]),
138 _ => None,
139 }
140}
141
142fn unaryop_opcodes(op: &str) -> Option<Vec<&'static str>> {
143 match op {
144 "!" => Some(vec!["OP_NOT"]),
145 "-" => Some(vec!["OP_NEGATE"]),
146 "~" => Some(vec!["OP_INVERT"]),
147 _ => None,
148 }
149}
150
151#[derive(Debug, Clone)]
158struct StackMap {
159 slots: Vec<String>,
160}
161
162impl StackMap {
163 fn new(initial: &[String]) -> Self {
164 StackMap {
165 slots: initial.to_vec(),
166 }
167 }
168
169 fn depth(&self) -> usize {
170 self.slots.len()
171 }
172
173 fn push(&mut self, name: &str) {
174 self.slots.push(name.to_string());
175 }
176
177 fn pop(&mut self) -> String {
178 self.slots.pop().expect("stack underflow")
179 }
180
181 fn find_depth(&self, name: &str) -> Option<usize> {
182 for (i, slot) in self.slots.iter().enumerate().rev() {
183 if slot == name {
184 return Some(self.slots.len() - 1 - i);
185 }
186 }
187 None
188 }
189
190 fn has(&self, name: &str) -> bool {
191 self.slots.iter().any(|s| s == name)
192 }
193
194 fn remove_at_depth(&mut self, depth_from_top: usize) -> String {
195 let index = self.slots.len() - 1 - depth_from_top;
196 self.slots.remove(index)
197 }
198
199 fn peek_at_depth(&self, depth_from_top: usize) -> &str {
200 let index = self.slots.len() - 1 - depth_from_top;
201 &self.slots[index]
202 }
203
204 fn rename_at_depth(&mut self, depth_from_top: usize, new_name: &str) {
205 let idx = self.slots.len() - 1 - depth_from_top;
206 self.slots[idx] = new_name.to_string();
207 }
208
209 fn swap(&mut self) {
210 let n = self.slots.len();
211 assert!(n >= 2, "stack underflow on swap");
212 self.slots.swap(n - 1, n - 2);
213 }
214
215 fn dup(&mut self) {
216 assert!(!self.slots.is_empty(), "stack underflow on dup");
217 let top = self.slots.last().unwrap().clone();
218 self.slots.push(top);
219 }
220
221 fn named_slots(&self) -> HashSet<String> {
223 self.slots.iter().filter(|s| !s.is_empty()).cloned().collect()
224 }
225}
226
227fn compute_last_uses(bindings: &[ANFBinding]) -> HashMap<String, usize> {
232 let mut last_use = HashMap::new();
233 for (i, binding) in bindings.iter().enumerate() {
234 for r in collect_refs(&binding.value) {
235 last_use.insert(r, i);
236 }
237 }
238 last_use
239}
240
241fn collect_refs(value: &ANFValue) -> Vec<String> {
242 let mut refs = Vec::new();
243 match value {
244 ANFValue::LoadParam { name } => {
245 refs.push(name.clone());
248 }
249 ANFValue::LoadProp { .. }
250 | ANFValue::GetStateScript { .. } => {}
251
252 ANFValue::LoadConst { value: v } => {
253 if let Some(s) = v.as_str() {
255 if s.len() > 5 && &s[..5] == "@ref:" {
256 refs.push(s[5..].to_string());
257 }
258 }
259 }
260
261 ANFValue::BinOp { left, right, .. } => {
262 refs.push(left.clone());
263 refs.push(right.clone());
264 }
265 ANFValue::UnaryOp { operand, .. } => {
266 refs.push(operand.clone());
267 }
268 ANFValue::Call { args, .. } => {
269 refs.extend(args.iter().cloned());
270 }
271 ANFValue::MethodCall { object, args, .. } => {
272 refs.push(object.clone());
273 refs.extend(args.iter().cloned());
274 }
275 ANFValue::If {
276 cond,
277 then,
278 else_branch,
279 } => {
280 refs.push(cond.clone());
281 for b in then {
282 refs.extend(collect_refs(&b.value));
283 }
284 for b in else_branch {
285 refs.extend(collect_refs(&b.value));
286 }
287 }
288 ANFValue::Loop { body, .. } => {
289 for b in body {
290 refs.extend(collect_refs(&b.value));
291 }
292 }
293 ANFValue::Assert { value } => {
294 refs.push(value.clone());
295 }
296 ANFValue::UpdateProp { value, .. } => {
297 refs.push(value.clone());
298 }
299 ANFValue::CheckPreimage { preimage } => {
300 refs.push(preimage.clone());
301 }
302 ANFValue::DeserializeState { preimage } => {
303 refs.push(preimage.clone());
304 }
305 ANFValue::AddOutput { satoshis, state_values, preimage } => {
306 refs.push(satoshis.clone());
307 refs.extend(state_values.iter().cloned());
308 if !preimage.is_empty() {
309 refs.push(preimage.clone());
310 }
311 }
312 ANFValue::AddRawOutput { satoshis, script_bytes } => {
313 refs.push(satoshis.clone());
314 refs.push(script_bytes.clone());
315 }
316 ANFValue::ArrayLiteral { elements } => {
317 refs.extend(elements.iter().cloned());
318 }
319 }
320 refs
321}
322
323struct LoweringContext {
328 sm: StackMap,
329 ops: Vec<StackOp>,
330 source_locs: Vec<Option<crate::ir::SourceLocation>>,
332 max_depth: usize,
333 properties: Vec<ANFProperty>,
334 private_methods: HashMap<String, ANFMethod>,
335 local_bindings: HashSet<String>,
338 outer_protected_refs: Option<HashSet<String>>,
340 inside_branch: bool,
343 current_source_loc: Option<crate::ir::SourceLocation>,
345}
346
347impl LoweringContext {
348 fn new(params: &[String], properties: &[ANFProperty]) -> Self {
349 let mut ctx = LoweringContext {
350 sm: StackMap::new(params),
351 ops: Vec::new(),
352 source_locs: Vec::new(),
353 max_depth: 0,
354 properties: properties.to_vec(),
355 private_methods: HashMap::new(),
356 local_bindings: HashSet::new(),
357 outer_protected_refs: None,
358 inside_branch: false,
359 current_source_loc: None,
360 };
361 ctx.track_depth();
362 ctx
363 }
364
365 fn track_depth(&mut self) {
366 if self.sm.depth() > self.max_depth {
367 self.max_depth = self.sm.depth();
368 }
369 }
370
371 fn emit_op(&mut self, op: StackOp) {
372 self.ops.push(op);
373 self.source_locs.push(self.current_source_loc.clone());
374 self.track_depth();
375 }
376
377 fn emit_varint_encoding(&mut self) {
387 self.emit_op(StackOp::Dup); self.sm.dup();
390 self.emit_op(StackOp::Push(PushValue::Int(253))); self.sm.push("");
392 self.emit_op(StackOp::Opcode("OP_LESSTHAN".into())); self.sm.pop();
394 self.sm.pop();
395 self.sm.push("");
396
397 self.emit_op(StackOp::Opcode("OP_IF".into()));
398 self.sm.pop(); self.emit_op(StackOp::Push(PushValue::Int(2))); self.sm.push("");
405 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into())); self.sm.pop();
407 self.sm.pop();
408 self.sm.push("");
409 self.emit_op(StackOp::Push(PushValue::Int(1))); self.sm.push("");
411 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
413 self.sm.pop();
414 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
418
419 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
420
421 self.emit_op(StackOp::Push(PushValue::Int(4))); self.sm.push("");
426 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into())); self.sm.pop();
428 self.sm.pop();
429 self.sm.push("");
430 self.emit_op(StackOp::Push(PushValue::Int(2))); self.sm.push("");
432 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
434 self.sm.pop();
435 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
439 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0xfd])));
440 self.sm.push("");
441 self.emit_op(StackOp::Swap);
442 self.sm.swap();
443 self.sm.pop();
444 self.sm.pop();
445 self.emit_op(StackOp::Opcode("OP_CAT".into()));
446 self.sm.push("");
447
448 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
449 }
451
452 fn emit_push_data_encode(&mut self) {
457 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
458 self.sm.push("");
459 self.emit_op(StackOp::Dup);
460 self.sm.push("");
461 self.emit_op(StackOp::Push(PushValue::Int(76)));
462 self.sm.push("");
463 self.emit_op(StackOp::Opcode("OP_LESSTHAN".into()));
464 self.sm.pop(); self.sm.pop();
465 self.sm.push("");
466
467 self.emit_op(StackOp::Opcode("OP_IF".into()));
468 self.sm.pop();
469 let sm_after_outer_if = self.sm.clone();
470
471 self.emit_op(StackOp::Push(PushValue::Int(2)));
473 self.sm.push("");
474 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
475 self.sm.pop(); self.sm.pop();
476 self.sm.push("");
477 self.emit_op(StackOp::Push(PushValue::Int(1)));
478 self.sm.push("");
479 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
480 self.sm.pop(); self.sm.pop();
481 self.sm.push(""); self.sm.push("");
482 self.emit_op(StackOp::Drop); self.sm.pop();
483 self.emit_op(StackOp::Swap); self.sm.swap();
484 self.sm.pop(); self.sm.pop();
485 self.emit_op(StackOp::Opcode("OP_CAT".into()));
486 self.sm.push("");
487 let sm_end_target = self.sm.clone();
488
489 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
490 self.sm = sm_after_outer_if.clone();
491
492 self.emit_op(StackOp::Dup);
493 self.sm.push("");
494 self.emit_op(StackOp::Push(PushValue::Int(256)));
495 self.sm.push("");
496 self.emit_op(StackOp::Opcode("OP_LESSTHAN".into()));
497 self.sm.pop(); self.sm.pop();
498 self.sm.push("");
499
500 self.emit_op(StackOp::Opcode("OP_IF".into()));
501 self.sm.pop();
502 let sm_after_inner_if = self.sm.clone();
503
504 self.emit_op(StackOp::Push(PushValue::Int(2)));
506 self.sm.push("");
507 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
508 self.sm.pop(); self.sm.pop();
509 self.sm.push("");
510 self.emit_op(StackOp::Push(PushValue::Int(1)));
511 self.sm.push("");
512 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
513 self.sm.pop(); self.sm.pop();
514 self.sm.push(""); self.sm.push("");
515 self.emit_op(StackOp::Drop); self.sm.pop();
516 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x4c])));
517 self.sm.push("");
518 self.emit_op(StackOp::Swap); self.sm.swap();
519 self.sm.pop(); self.sm.pop();
520 self.emit_op(StackOp::Opcode("OP_CAT".into()));
521 self.sm.push("");
522 self.emit_op(StackOp::Swap); self.sm.swap();
523 self.sm.pop(); self.sm.pop();
524 self.emit_op(StackOp::Opcode("OP_CAT".into()));
525 self.sm.push("");
526
527 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
528 self.sm = sm_after_inner_if;
529
530 self.emit_op(StackOp::Push(PushValue::Int(4)));
532 self.sm.push("");
533 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
534 self.sm.pop(); self.sm.pop();
535 self.sm.push("");
536 self.emit_op(StackOp::Push(PushValue::Int(2)));
537 self.sm.push("");
538 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
539 self.sm.pop(); self.sm.pop();
540 self.sm.push(""); self.sm.push("");
541 self.emit_op(StackOp::Drop); self.sm.pop();
542 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x4d])));
543 self.sm.push("");
544 self.emit_op(StackOp::Swap); self.sm.swap();
545 self.sm.pop(); self.sm.pop();
546 self.emit_op(StackOp::Opcode("OP_CAT".into()));
547 self.sm.push("");
548 self.emit_op(StackOp::Swap); self.sm.swap();
549 self.sm.pop(); self.sm.pop();
550 self.emit_op(StackOp::Opcode("OP_CAT".into()));
551 self.sm.push("");
552
553 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
554 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
555 self.sm = sm_end_target;
556 }
557
558 fn emit_push_data_decode(&mut self) {
563 self.emit_op(StackOp::Push(PushValue::Int(1)));
564 self.sm.push("");
565 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
566 self.sm.pop(); self.sm.pop();
567 self.sm.push(""); self.sm.push("");
568 self.emit_op(StackOp::Swap); self.sm.swap();
569 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
570 self.emit_op(StackOp::Dup);
571 self.sm.push("");
572 self.emit_op(StackOp::Push(PushValue::Int(76)));
573 self.sm.push("");
574 self.emit_op(StackOp::Opcode("OP_LESSTHAN".into()));
575 self.sm.pop(); self.sm.pop();
576 self.sm.push("");
577
578 self.emit_op(StackOp::Opcode("OP_IF".into()));
579 self.sm.pop();
580 let sm_after_outer_if = self.sm.clone();
581
582 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
584 self.sm.pop(); self.sm.pop();
585 self.sm.push(""); self.sm.push("");
586 let sm_end_target = self.sm.clone();
587
588 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
589 self.sm = sm_after_outer_if.clone();
590
591 self.emit_op(StackOp::Dup);
592 self.sm.push("");
593 self.emit_op(StackOp::Push(PushValue::Int(77)));
594 self.sm.push("");
595 self.emit_op(StackOp::Opcode("OP_NUMEQUAL".into()));
596 self.sm.pop(); self.sm.pop();
597 self.sm.push("");
598
599 self.emit_op(StackOp::Opcode("OP_IF".into()));
600 self.sm.pop();
601 let sm_after_inner_if = self.sm.clone();
602
603 self.emit_op(StackOp::Drop); self.sm.pop();
605 self.emit_op(StackOp::Push(PushValue::Int(2)));
606 self.sm.push("");
607 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
608 self.sm.pop(); self.sm.pop();
609 self.sm.push(""); self.sm.push("");
610 self.emit_op(StackOp::Swap); self.sm.swap();
611 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
612 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
613 self.sm.pop(); self.sm.pop();
614 self.sm.push(""); self.sm.push("");
615
616 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
617 self.sm = sm_after_inner_if;
618
619 self.emit_op(StackOp::Drop); self.sm.pop();
621 self.emit_op(StackOp::Push(PushValue::Int(1)));
622 self.sm.push("");
623 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
624 self.sm.pop(); self.sm.pop();
625 self.sm.push(""); self.sm.push("");
626 self.emit_op(StackOp::Swap); self.sm.swap();
627 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
628 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
629 self.sm.pop(); self.sm.pop();
630 self.sm.push(""); self.sm.push("");
631
632 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
633 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
634 self.sm = sm_end_target;
635 }
636
637 fn is_last_use(&self, name: &str, current_index: usize, last_uses: &HashMap<String, usize>) -> bool {
638 match last_uses.get(name) {
639 None => true,
640 Some(&last) => last <= current_index,
641 }
642 }
643
644 fn bring_to_top(&mut self, name: &str, consume: bool) {
645 let depth = self
646 .sm
647 .find_depth(name)
648 .unwrap_or_else(|| panic!("value '{}' not found on stack", name));
649
650 if depth == 0 {
651 if !consume {
652 self.emit_op(StackOp::Dup);
653 self.sm.dup();
654 }
655 return;
656 }
657
658 if depth == 1 && consume {
659 self.emit_op(StackOp::Swap);
660 self.sm.swap();
661 return;
662 }
663
664 if consume {
665 if depth == 2 {
666 self.emit_op(StackOp::Rot);
667 let removed = self.sm.remove_at_depth(2);
668 self.sm.push(&removed);
669 } else {
670 self.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
671 self.sm.push(""); self.emit_op(StackOp::Roll { depth });
673 self.sm.pop(); let rolled = self.sm.remove_at_depth(depth);
675 self.sm.push(&rolled);
676 }
677 } else {
678 if depth == 1 {
679 self.emit_op(StackOp::Over);
680 let picked = self.sm.peek_at_depth(1).to_string();
681 self.sm.push(&picked);
682 } else {
683 self.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
684 self.sm.push(""); self.emit_op(StackOp::Pick { depth });
686 self.sm.pop(); let picked = self.sm.peek_at_depth(depth).to_string();
688 self.sm.push(&picked);
689 }
690 }
691
692 self.track_depth();
693 }
694
695 fn lower_bindings(&mut self, bindings: &[ANFBinding], terminal_assert: bool) {
700 self.local_bindings = bindings.iter().map(|b| b.name.clone()).collect();
701 let mut last_uses = compute_last_uses(bindings);
702
703 if let Some(ref protected) = self.outer_protected_refs {
705 for r in protected {
706 last_uses.insert(r.clone(), bindings.len());
707 }
708 }
709
710 let mut last_assert_idx: isize = -1;
714 let mut terminal_if_idx: isize = -1;
715 if terminal_assert {
716 let last_binding = bindings.last();
717 if let Some(b) = last_binding {
718 if matches!(&b.value, ANFValue::If { .. }) {
719 terminal_if_idx = (bindings.len() - 1) as isize;
720 } else {
721 for i in (0..bindings.len()).rev() {
722 if matches!(&bindings[i].value, ANFValue::Assert { .. }) {
723 last_assert_idx = i as isize;
724 break;
725 }
726 }
727 }
728 }
729 }
730
731 for (i, binding) in bindings.iter().enumerate() {
732 self.current_source_loc = binding.source_loc.clone();
734
735 if matches!(&binding.value, ANFValue::Assert { .. }) && i as isize == last_assert_idx {
736 if let ANFValue::Assert { value } = &binding.value {
738 self.lower_assert(value, i, &last_uses, true);
739 }
740 } else if matches!(&binding.value, ANFValue::If { .. }) && i as isize == terminal_if_idx {
741 if let ANFValue::If { cond, then, else_branch } = &binding.value {
743 self.lower_if(&binding.name, cond, then, else_branch, i, &last_uses, true);
744 }
745 } else {
746 self.lower_binding(binding, i, &last_uses);
747 }
748 }
749 }
750
751 fn lower_binding(
752 &mut self,
753 binding: &ANFBinding,
754 binding_index: usize,
755 last_uses: &HashMap<String, usize>,
756 ) {
757 let name = &binding.name;
758 match &binding.value {
759 ANFValue::LoadParam {
760 name: param_name, ..
761 } => {
762 self.lower_load_param(name, param_name, binding_index, last_uses);
763 }
764 ANFValue::LoadProp {
765 name: prop_name, ..
766 } => {
767 self.lower_load_prop(name, prop_name);
768 }
769 ANFValue::LoadConst { .. } => {
770 self.lower_load_const(name, &binding.value, binding_index, last_uses);
771 }
772 ANFValue::BinOp {
773 op, left, right, result_type, ..
774 } => {
775 self.lower_bin_op(name, op, left, right, binding_index, last_uses, result_type.as_deref());
776 }
777 ANFValue::UnaryOp { op, operand, .. } => {
778 self.lower_unary_op(name, op, operand, binding_index, last_uses);
779 }
780 ANFValue::Call {
781 func: func_name,
782 args,
783 } => {
784 self.lower_call(name, func_name, args, binding_index, last_uses);
785 }
786 ANFValue::MethodCall {
787 object,
788 method,
789 args,
790 } => {
791 self.lower_method_call(name, object, method, args, binding_index, last_uses);
792 }
793 ANFValue::If {
794 cond,
795 then,
796 else_branch,
797 } => {
798 self.lower_if(name, cond, then, else_branch, binding_index, last_uses, false);
799 }
800 ANFValue::Loop {
801 count,
802 body,
803 iter_var,
804 } => {
805 self.lower_loop(name, *count, body, iter_var);
806 }
807 ANFValue::Assert { value } => {
808 self.lower_assert(value, binding_index, last_uses, false);
809 }
810 ANFValue::UpdateProp {
811 name: prop_name,
812 value,
813 } => {
814 self.lower_update_prop(prop_name, value, binding_index, last_uses);
815 }
816 ANFValue::GetStateScript {} => {
817 self.lower_get_state_script(name);
818 }
819 ANFValue::CheckPreimage { preimage } => {
820 self.lower_check_preimage(name, preimage, binding_index, last_uses);
821 }
822 ANFValue::DeserializeState { preimage } => {
823 self.lower_deserialize_state(preimage, binding_index, last_uses);
824 }
825 ANFValue::AddOutput { satoshis, state_values, preimage } => {
826 self.lower_add_output(name, satoshis, state_values, preimage, binding_index, last_uses);
827 }
828 ANFValue::AddRawOutput { satoshis, script_bytes } => {
829 self.lower_add_raw_output(name, satoshis, script_bytes, binding_index, last_uses);
830 }
831 ANFValue::ArrayLiteral { elements } => {
832 self.lower_array_literal(name, elements, binding_index, last_uses);
833 }
834 }
835 }
836
837 fn lower_load_param(
842 &mut self,
843 binding_name: &str,
844 param_name: &str,
845 binding_index: usize,
846 last_uses: &HashMap<String, usize>,
847 ) {
848 if self.sm.has(param_name) {
849 let is_last = self.is_last_use(param_name, binding_index, last_uses);
850 self.bring_to_top(param_name, is_last);
851 self.sm.pop();
852 self.sm.push(binding_name);
853 } else {
854 self.emit_op(StackOp::Push(PushValue::Int(0)));
855 self.sm.push(binding_name);
856 }
857 }
858
859 fn lower_load_prop(&mut self, binding_name: &str, prop_name: &str) {
860 let prop = self.properties.iter().find(|p| p.name == prop_name).cloned();
861
862 if self.sm.has(prop_name) {
863 self.bring_to_top(prop_name, false);
867 self.sm.pop();
868 } else if let Some(ref p) = prop {
869 if let Some(ref val) = p.initial_value {
870 self.push_json_value(val);
871 } else {
872 let param_index = self
875 .properties
876 .iter()
877 .position(|p2| p2.name == prop_name)
878 .unwrap_or(0);
879 self.emit_op(StackOp::Placeholder {
880 param_index,
881 param_name: prop_name.to_string(),
882 });
883 }
884 } else {
885 let param_index = self
887 .properties
888 .iter()
889 .position(|p2| p2.name == prop_name)
890 .unwrap_or(0);
891 self.emit_op(StackOp::Placeholder {
892 param_index,
893 param_name: prop_name.to_string(),
894 });
895 }
896 self.sm.push(binding_name);
897 }
898
899 fn push_json_value(&mut self, val: &serde_json::Value) {
900 match val {
901 serde_json::Value::Bool(b) => {
902 self.emit_op(StackOp::Push(PushValue::Bool(*b)));
903 }
904 serde_json::Value::Number(n) => {
905 let i = n.as_i64().map(|v| v as i128).unwrap_or(0);
906 self.emit_op(StackOp::Push(PushValue::Int(i)));
907 }
908 serde_json::Value::String(s) => {
909 let bytes = hex_to_bytes(s);
910 self.emit_op(StackOp::Push(PushValue::Bytes(bytes)));
911 }
912 _ => {
913 self.emit_op(StackOp::Push(PushValue::Int(0)));
914 }
915 }
916 }
917
918 fn lower_load_const(&mut self, binding_name: &str, value: &ANFValue, binding_index: usize, last_uses: &HashMap<String, usize>) {
919 if let Some(ConstValue::Str(ref s)) = value.const_value() {
924 if s.len() > 5 && &s[..5] == "@ref:" {
925 let ref_name = &s[5..];
926 if self.sm.has(ref_name) {
927 let consume = self.local_bindings.contains(ref_name)
932 && self.is_last_use(ref_name, binding_index, last_uses);
933 self.bring_to_top(ref_name, consume);
934 self.sm.pop();
935 self.sm.push(binding_name);
936 } else {
937 self.emit_op(StackOp::Push(PushValue::Int(0)));
939 self.sm.push(binding_name);
940 }
941 return;
942 }
943 if s == "@this" {
945 self.emit_op(StackOp::Push(PushValue::Int(0)));
946 self.sm.push(binding_name);
947 return;
948 }
949 }
950
951 match value.const_value() {
952 Some(ConstValue::Bool(b)) => {
953 self.emit_op(StackOp::Push(PushValue::Bool(b)));
954 }
955 Some(ConstValue::Int(n)) => {
956 self.emit_op(StackOp::Push(PushValue::Int(n)));
957 }
958 Some(ConstValue::Str(s)) => {
959 let bytes = hex_to_bytes(&s);
960 self.emit_op(StackOp::Push(PushValue::Bytes(bytes)));
961 }
962 None => {
963 self.emit_op(StackOp::Push(PushValue::Int(0)));
964 }
965 }
966 self.sm.push(binding_name);
967 }
968
969 fn lower_bin_op(
970 &mut self,
971 binding_name: &str,
972 op: &str,
973 left: &str,
974 right: &str,
975 binding_index: usize,
976 last_uses: &HashMap<String, usize>,
977 result_type: Option<&str>,
978 ) {
979 let left_is_last = self.is_last_use(left, binding_index, last_uses);
980 self.bring_to_top(left, left_is_last);
981
982 let right_is_last = self.is_last_use(right, binding_index, last_uses);
983 self.bring_to_top(right, right_is_last);
984
985 self.sm.pop();
986 self.sm.pop();
987
988 if result_type == Some("bytes") && op == "+" {
991 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
992 } else if result_type == Some("bytes") && (op == "===" || op == "!==") {
993 self.emit_op(StackOp::Opcode("OP_EQUAL".to_string()));
994 if op == "!==" {
995 self.emit_op(StackOp::Opcode("OP_NOT".to_string()));
996 }
997 } else {
998 let codes = binop_opcodes(op)
999 .unwrap_or_else(|| panic!("unknown binary operator: {}", op));
1000 for code in codes {
1001 self.emit_op(StackOp::Opcode(code.to_string()));
1002 }
1003 }
1004
1005 self.sm.push(binding_name);
1006 self.track_depth();
1007 }
1008
1009 fn lower_unary_op(
1010 &mut self,
1011 binding_name: &str,
1012 op: &str,
1013 operand: &str,
1014 binding_index: usize,
1015 last_uses: &HashMap<String, usize>,
1016 ) {
1017 let is_last = self.is_last_use(operand, binding_index, last_uses);
1018 self.bring_to_top(operand, is_last);
1019
1020 self.sm.pop();
1021
1022 let codes = unaryop_opcodes(op)
1023 .unwrap_or_else(|| panic!("unknown unary operator: {}", op));
1024 for code in codes {
1025 self.emit_op(StackOp::Opcode(code.to_string()));
1026 }
1027
1028 self.sm.push(binding_name);
1029 self.track_depth();
1030 }
1031
1032 fn lower_call(
1033 &mut self,
1034 binding_name: &str,
1035 func_name: &str,
1036 args: &[String],
1037 binding_index: usize,
1038 last_uses: &HashMap<String, usize>,
1039 ) {
1040 if func_name == "assert" {
1042 if !args.is_empty() {
1043 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
1044 self.bring_to_top(&args[0], is_last);
1045 self.sm.pop();
1046 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
1047 self.sm.push(binding_name);
1048 }
1049 return;
1050 }
1051
1052 if func_name == "super" {
1055 self.sm.push(binding_name);
1056 return;
1057 }
1058
1059 if func_name == "checkMultiSig" && args.len() == 2 {
1061 self.lower_check_multi_sig(binding_name, args, binding_index, last_uses);
1062 return;
1063 }
1064
1065 if func_name == "__array_access" {
1066 self.lower_array_access(binding_name, args, binding_index, last_uses);
1067 return;
1068 }
1069
1070 if func_name == "reverseBytes" {
1071 self.lower_reverse_bytes(binding_name, args, binding_index, last_uses);
1072 return;
1073 }
1074
1075 if func_name == "substr" {
1076 self.lower_substr(binding_name, args, binding_index, last_uses);
1077 return;
1078 }
1079
1080 if func_name == "verifyRabinSig" {
1081 self.lower_verify_rabin_sig(binding_name, args, binding_index, last_uses);
1082 return;
1083 }
1084
1085 if func_name == "verifyWOTS" {
1086 self.lower_verify_wots(binding_name, args, binding_index, last_uses);
1087 return;
1088 }
1089
1090 if func_name.starts_with("verifySLHDSA_") {
1091 let param_key = func_name.trim_start_matches("verifySLHDSA_");
1092 self.lower_verify_slh_dsa(binding_name, param_key, args, binding_index, last_uses);
1093 return;
1094 }
1095
1096 if func_name == "sha256Compress" {
1097 self.lower_sha256_compress(binding_name, args, binding_index, last_uses);
1098 return;
1099 }
1100
1101 if func_name == "sha256Finalize" {
1102 self.lower_sha256_finalize(binding_name, args, binding_index, last_uses);
1103 return;
1104 }
1105
1106 if func_name == "blake3Compress" {
1107 self.lower_blake3_compress(binding_name, args, binding_index, last_uses);
1108 return;
1109 }
1110
1111 if func_name == "blake3Hash" {
1112 self.lower_blake3_hash(binding_name, args, binding_index, last_uses);
1113 return;
1114 }
1115
1116 if is_ec_builtin(func_name) {
1117 self.lower_ec_builtin(binding_name, func_name, args, binding_index, last_uses);
1118 return;
1119 }
1120
1121 if func_name == "safediv" {
1122 self.lower_safediv(binding_name, args, binding_index, last_uses);
1123 return;
1124 }
1125
1126 if func_name == "safemod" {
1127 self.lower_safemod(binding_name, args, binding_index, last_uses);
1128 return;
1129 }
1130
1131 if func_name == "clamp" {
1132 self.lower_clamp(binding_name, args, binding_index, last_uses);
1133 return;
1134 }
1135
1136 if func_name == "pow" {
1137 self.lower_pow(binding_name, args, binding_index, last_uses);
1138 return;
1139 }
1140
1141 if func_name == "mulDiv" {
1142 self.lower_mul_div(binding_name, args, binding_index, last_uses);
1143 return;
1144 }
1145
1146 if func_name == "percentOf" {
1147 self.lower_percent_of(binding_name, args, binding_index, last_uses);
1148 return;
1149 }
1150
1151 if func_name == "sqrt" {
1152 self.lower_sqrt(binding_name, args, binding_index, last_uses);
1153 return;
1154 }
1155
1156 if func_name == "gcd" {
1157 self.lower_gcd(binding_name, args, binding_index, last_uses);
1158 return;
1159 }
1160
1161 if func_name == "divmod" {
1162 self.lower_divmod(binding_name, args, binding_index, last_uses);
1163 return;
1164 }
1165
1166 if func_name == "log2" {
1167 self.lower_log2(binding_name, args, binding_index, last_uses);
1168 return;
1169 }
1170
1171 if func_name == "sign" {
1172 self.lower_sign(binding_name, args, binding_index, last_uses);
1173 return;
1174 }
1175
1176 if func_name == "right" {
1177 self.lower_right(binding_name, args, binding_index, last_uses);
1178 return;
1179 }
1180
1181 if func_name == "pack" || func_name == "toByteString" {
1184 if !args.is_empty() {
1185 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
1186 self.bring_to_top(&args[0], is_last);
1187 self.sm.pop();
1188 }
1189 self.sm.push(binding_name);
1190 return;
1191 }
1192
1193 if func_name == "computeStateOutputHash" {
1196 self.lower_compute_state_output_hash(binding_name, args, binding_index, last_uses);
1197 return;
1198 }
1199
1200 if func_name == "computeStateOutput" {
1204 self.lower_compute_state_output(binding_name, args, binding_index, last_uses);
1205 return;
1206 }
1207
1208 if func_name == "buildChangeOutput" {
1212 self.lower_build_change_output(binding_name, args, binding_index, last_uses);
1213 return;
1214 }
1215
1216 if func_name.starts_with("extract") {
1220 self.lower_extractor(binding_name, func_name, args, binding_index, last_uses);
1221 return;
1222 }
1223
1224 for arg in args {
1226 let is_last = self.is_last_use(arg, binding_index, last_uses);
1227 self.bring_to_top(arg, is_last);
1228 }
1229
1230 for _ in args {
1231 self.sm.pop();
1232 }
1233
1234 if let Some(codes) = builtin_opcodes(func_name) {
1235 for code in codes {
1236 self.emit_op(StackOp::Opcode(code.to_string()));
1237 }
1238 } else {
1239 self.emit_op(StackOp::Push(PushValue::Int(0)));
1241 self.sm.push(binding_name);
1242 return;
1243 }
1244
1245 if func_name == "split" {
1246 self.sm.push("");
1247 self.sm.push(binding_name);
1248 } else if func_name == "len" {
1249 self.sm.push("");
1250 self.sm.push(binding_name);
1251 } else {
1252 self.sm.push(binding_name);
1253 }
1254
1255 self.track_depth();
1256 }
1257
1258 fn lower_method_call(
1259 &mut self,
1260 binding_name: &str,
1261 object: &str,
1262 method: &str,
1263 args: &[String],
1264 binding_index: usize,
1265 last_uses: &HashMap<String, usize>,
1266 ) {
1267 if self.sm.has(object) {
1270 self.bring_to_top(object, true);
1271 self.emit_op(StackOp::Drop);
1272 self.sm.pop();
1273 }
1274
1275 if method == "getStateScript" {
1276 self.lower_get_state_script(binding_name);
1277 return;
1278 }
1279
1280 if let Some(private_method) = self.private_methods.get(method).cloned() {
1282 self.inline_method_call(binding_name, &private_method, args, binding_index, last_uses);
1283 return;
1284 }
1285
1286 self.lower_call(binding_name, method, args, binding_index, last_uses);
1288 }
1289
1290 fn inline_method_call(
1293 &mut self,
1294 binding_name: &str,
1295 method: &ANFMethod,
1296 args: &[String],
1297 binding_index: usize,
1298 last_uses: &HashMap<String, usize>,
1299 ) {
1300 let mut shadowed: Vec<(String, String)> = Vec::new();
1305
1306 for (i, arg) in args.iter().enumerate() {
1308 if i < method.params.len() {
1309 let is_last = self.is_last_use(arg, binding_index, last_uses);
1310 self.bring_to_top(arg, is_last);
1311 self.sm.pop();
1312
1313 let param_name = &method.params[i].name;
1314
1315 if self.sm.has(param_name) {
1318 let existing_depth = self.sm.find_depth(param_name).unwrap();
1319 let shadowed_name = format!("__shadowed_{}_{}", binding_index, param_name);
1320 self.sm.rename_at_depth(existing_depth, &shadowed_name);
1321 shadowed.push((param_name.clone(), shadowed_name));
1322 }
1323
1324 self.sm.push(param_name);
1326 }
1327 }
1328
1329 self.lower_bindings(&method.body, false);
1331
1332 for (param_name, shadowed_name) in &shadowed {
1334 if self.sm.has(shadowed_name) {
1335 let depth = self.sm.find_depth(shadowed_name).unwrap();
1336 self.sm.rename_at_depth(depth, param_name);
1337 }
1338 }
1339
1340 if !method.body.is_empty() {
1343 let last_binding_name = &method.body[method.body.len() - 1].name;
1344 if self.sm.depth() > 0 {
1345 let top_name = self.sm.peek_at_depth(0).to_string();
1346 if top_name == *last_binding_name {
1347 self.sm.pop();
1348 self.sm.push(binding_name);
1349 }
1350 }
1351 }
1352 }
1353
1354 fn lower_if(
1355 &mut self,
1356 binding_name: &str,
1357 cond: &str,
1358 then_bindings: &[ANFBinding],
1359 else_bindings: &[ANFBinding],
1360 binding_index: usize,
1361 last_uses: &HashMap<String, usize>,
1362 terminal_assert: bool,
1363 ) {
1364 let is_last = self.is_last_use(cond, binding_index, last_uses);
1365 self.bring_to_top(cond, is_last);
1366 self.sm.pop(); let mut protected_refs = HashSet::new();
1370 for (ref_name, &last_idx) in last_uses.iter() {
1371 if last_idx > binding_index && self.sm.has(ref_name) {
1372 protected_refs.insert(ref_name.clone());
1373 }
1374 }
1375
1376 let pre_if_names = self.sm.named_slots();
1378
1379 let mut then_ctx = LoweringContext::new(&[], &self.properties);
1381 then_ctx.sm = self.sm.clone();
1382 then_ctx.outer_protected_refs = Some(protected_refs.clone());
1383 then_ctx.inside_branch = true;
1384 then_ctx.lower_bindings(then_bindings, terminal_assert);
1385
1386 if terminal_assert && then_ctx.sm.depth() > 1 {
1387 let excess = then_ctx.sm.depth() - 1;
1388 for _ in 0..excess {
1389 then_ctx.emit_op(StackOp::Nip);
1390 then_ctx.sm.remove_at_depth(1);
1391 }
1392 }
1393
1394 let mut else_ctx = LoweringContext::new(&[], &self.properties);
1396 else_ctx.sm = self.sm.clone();
1397 else_ctx.outer_protected_refs = Some(protected_refs);
1398 else_ctx.inside_branch = true;
1399 else_ctx.lower_bindings(else_bindings, terminal_assert);
1400
1401 if terminal_assert && else_ctx.sm.depth() > 1 {
1402 let excess = else_ctx.sm.depth() - 1;
1403 for _ in 0..excess {
1404 else_ctx.emit_op(StackOp::Nip);
1405 else_ctx.sm.remove_at_depth(1);
1406 }
1407 }
1408
1409 let post_then_names = then_ctx.sm.named_slots();
1422 let mut consumed_names: Vec<String> = Vec::new();
1423 for name in &pre_if_names {
1424 if !post_then_names.contains(name) && else_ctx.sm.has(name) {
1425 consumed_names.push(name.clone());
1426 }
1427 }
1428 let post_else_names = else_ctx.sm.named_slots();
1429 let mut else_consumed_names: Vec<String> = Vec::new();
1430 for name in &pre_if_names {
1431 if !post_else_names.contains(name) && then_ctx.sm.has(name) {
1432 else_consumed_names.push(name.clone());
1433 }
1434 }
1435
1436 if !consumed_names.is_empty() {
1439 let mut depths: Vec<usize> = consumed_names
1440 .iter()
1441 .map(|n| else_ctx.sm.find_depth(n).unwrap())
1442 .collect();
1443 depths.sort_by(|a, b| b.cmp(a));
1444 for depth in depths {
1445 if depth == 0 {
1446 else_ctx.emit_op(StackOp::Drop);
1447 else_ctx.sm.pop();
1448 } else if depth == 1 {
1449 else_ctx.emit_op(StackOp::Nip);
1450 else_ctx.sm.remove_at_depth(1);
1451 } else {
1452 else_ctx.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
1453 else_ctx.sm.push("");
1454 else_ctx.emit_op(StackOp::Roll { depth });
1455 else_ctx.sm.pop();
1456 let rolled = else_ctx.sm.remove_at_depth(depth);
1457 else_ctx.sm.push(&rolled);
1458 else_ctx.emit_op(StackOp::Drop);
1459 else_ctx.sm.pop();
1460 }
1461 }
1462 }
1463 if !else_consumed_names.is_empty() {
1464 let mut depths: Vec<usize> = else_consumed_names
1465 .iter()
1466 .map(|n| then_ctx.sm.find_depth(n).unwrap())
1467 .collect();
1468 depths.sort_by(|a, b| b.cmp(a));
1469 for depth in depths {
1470 if depth == 0 {
1471 then_ctx.emit_op(StackOp::Drop);
1472 then_ctx.sm.pop();
1473 } else if depth == 1 {
1474 then_ctx.emit_op(StackOp::Nip);
1475 then_ctx.sm.remove_at_depth(1);
1476 } else {
1477 then_ctx.emit_op(StackOp::Push(PushValue::Int(depth as i128)));
1478 then_ctx.sm.push("");
1479 then_ctx.emit_op(StackOp::Roll { depth });
1480 then_ctx.sm.pop();
1481 let rolled = then_ctx.sm.remove_at_depth(depth);
1482 then_ctx.sm.push(&rolled);
1483 then_ctx.emit_op(StackOp::Drop);
1484 then_ctx.sm.pop();
1485 }
1486 }
1487 }
1488
1489 if then_ctx.sm.depth() > else_ctx.sm.depth() {
1492 let then_top_p3 = then_ctx.sm.peek_at_depth(0).to_string();
1496 if else_bindings.is_empty() && !then_top_p3.is_empty() && else_ctx.sm.has(&then_top_p3) {
1497 let var_depth = else_ctx.sm.find_depth(&then_top_p3).unwrap();
1498 if var_depth == 0 {
1499 else_ctx.emit_op(StackOp::Dup);
1500 } else {
1501 else_ctx.emit_op(StackOp::Push(PushValue::Int(var_depth as i128)));
1502 else_ctx.sm.push("");
1503 else_ctx.emit_op(StackOp::Pick { depth: var_depth });
1504 else_ctx.sm.pop();
1505 }
1506 else_ctx.sm.push(&then_top_p3);
1507 } else {
1508 else_ctx.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1509 else_ctx.sm.push("");
1510 }
1511 } else if else_ctx.sm.depth() > then_ctx.sm.depth() {
1512 then_ctx.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1513 then_ctx.sm.push("");
1514 }
1515
1516 let then_ops = then_ctx.ops;
1517 let else_ops = else_ctx.ops;
1518
1519 self.emit_op(StackOp::If {
1520 then_ops,
1521 else_ops: if else_ops.is_empty() {
1522 Vec::new()
1523 } else {
1524 else_ops
1525 },
1526 });
1527
1528 let post_branch_names = then_ctx.sm.named_slots();
1530 for name in &pre_if_names {
1531 if !post_branch_names.contains(name) && self.sm.has(name) {
1532 if let Some(depth) = self.sm.find_depth(name) {
1533 self.sm.remove_at_depth(depth);
1534 }
1535 }
1536 }
1537
1538 if then_ctx.sm.depth() > self.sm.depth() {
1540 let then_top = then_ctx.sm.peek_at_depth(0).to_string();
1541 let else_top = if else_ctx.sm.depth() > 0 {
1542 else_ctx.sm.peek_at_depth(0).to_string()
1543 } else {
1544 String::new()
1545 };
1546 let is_property = self.properties.iter().any(|p| p.name == then_top);
1547 if is_property && !then_top.is_empty() && then_top == else_top
1548 && then_top != binding_name && self.sm.has(&then_top)
1549 {
1550 self.sm.push(&then_top);
1552 for d in 1..self.sm.depth() {
1553 if self.sm.peek_at_depth(d) == then_top {
1554 if d == 1 {
1555 self.emit_op(StackOp::Nip);
1556 self.sm.remove_at_depth(1);
1557 } else {
1558 self.emit_op(StackOp::Push(PushValue::Int(d as i128)));
1559 self.sm.push("");
1560 self.emit_op(StackOp::Roll { depth: d + 1 });
1561 self.sm.pop();
1562 let rolled = self.sm.remove_at_depth(d);
1563 self.sm.push(&rolled);
1564 self.emit_op(StackOp::Drop);
1565 self.sm.pop();
1566 }
1567 break;
1568 }
1569 }
1570 } else if !then_top.is_empty() && !is_property && else_bindings.is_empty()
1571 && then_top != binding_name && self.sm.has(&then_top)
1572 {
1573 self.sm.push(&then_top);
1577 for d in 1..self.sm.depth() {
1578 if self.sm.peek_at_depth(d) == then_top {
1579 if d == 1 {
1580 self.emit_op(StackOp::Nip);
1581 self.sm.remove_at_depth(1);
1582 } else {
1583 self.emit_op(StackOp::Push(PushValue::Int(d as i128)));
1584 self.sm.push("");
1585 self.emit_op(StackOp::Roll { depth: d + 1 });
1586 self.sm.pop();
1587 let rolled = self.sm.remove_at_depth(d);
1588 self.sm.push(&rolled);
1589 self.emit_op(StackOp::Drop);
1590 self.sm.pop();
1591 }
1592 break;
1593 }
1594 }
1595 } else {
1596 self.sm.push(binding_name);
1597 }
1598 } else if else_ctx.sm.depth() > self.sm.depth() {
1599 self.sm.push(binding_name);
1600 } else {
1601 }
1603 self.track_depth();
1604
1605 if then_ctx.max_depth > self.max_depth {
1606 self.max_depth = then_ctx.max_depth;
1607 }
1608 if else_ctx.max_depth > self.max_depth {
1609 self.max_depth = else_ctx.max_depth;
1610 }
1611 }
1612
1613 fn lower_loop(
1614 &mut self,
1615 _binding_name: &str,
1616 count: usize,
1617 body: &[ANFBinding],
1618 iter_var: &str,
1619 ) {
1620 let body_binding_names: HashSet<String> = body.iter().map(|b| b.name.clone()).collect();
1623 let mut outer_refs = HashSet::new();
1624 for b in body {
1625 if let ANFValue::LoadParam { name } = &b.value {
1626 if name != iter_var {
1627 outer_refs.insert(name.clone());
1628 }
1629 }
1630 if let ANFValue::LoadConst { value: v } = &b.value {
1632 if let Some(s) = v.as_str() {
1633 if s.len() > 5 && &s[..5] == "@ref:" {
1634 let ref_name = &s[5..];
1635 if !body_binding_names.contains(ref_name) {
1636 outer_refs.insert(ref_name.to_string());
1637 }
1638 }
1639 }
1640 }
1641 }
1642
1643 let prev_local_bindings = self.local_bindings.clone();
1646 self.local_bindings = self.local_bindings.union(&body_binding_names).cloned().collect();
1647
1648 for i in 0..count {
1649 self.emit_op(StackOp::Push(PushValue::Int(i as i128)));
1650 self.sm.push(iter_var);
1651
1652 let mut last_uses = compute_last_uses(body);
1653
1654 if i < count - 1 {
1657 for ref_name in &outer_refs {
1658 last_uses.insert(ref_name.clone(), body.len());
1659 }
1660 }
1661
1662 for (j, binding) in body.iter().enumerate() {
1663 self.lower_binding(binding, j, &last_uses);
1664 }
1665
1666 if self.sm.has(iter_var) {
1669 let depth = self.sm.find_depth(iter_var);
1670 if let Some(0) = depth {
1671 self.emit_op(StackOp::Drop);
1672 self.sm.pop();
1673 }
1674 }
1675 }
1676 self.local_bindings = prev_local_bindings;
1678 }
1682
1683 fn lower_assert(
1684 &mut self,
1685 value_ref: &str,
1686 binding_index: usize,
1687 last_uses: &HashMap<String, usize>,
1688 terminal: bool,
1689 ) {
1690 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1691 self.bring_to_top(value_ref, is_last);
1692 if terminal {
1693 } else {
1696 self.sm.pop();
1697 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
1698 }
1699 self.track_depth();
1700 }
1701
1702 fn lower_update_prop(
1703 &mut self,
1704 prop_name: &str,
1705 value_ref: &str,
1706 binding_index: usize,
1707 last_uses: &HashMap<String, usize>,
1708 ) {
1709 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
1710 self.bring_to_top(value_ref, is_last);
1711 self.sm.pop();
1712 self.sm.push(prop_name);
1713
1714 if !self.inside_branch {
1721 for d in 1..self.sm.depth() {
1722 if self.sm.peek_at_depth(d) == prop_name {
1723 if d == 1 {
1724 self.emit_op(StackOp::Nip);
1725 self.sm.remove_at_depth(1);
1726 } else {
1727 self.emit_op(StackOp::Push(PushValue::Int(d as i128)));
1728 self.sm.push("");
1729 self.emit_op(StackOp::Roll { depth: d + 1 });
1730 self.sm.pop();
1731 let rolled = self.sm.remove_at_depth(d);
1732 self.sm.push(&rolled);
1733 self.emit_op(StackOp::Drop);
1734 self.sm.pop();
1735 }
1736 break;
1737 }
1738 }
1739 }
1740
1741 self.track_depth();
1742 }
1743
1744 fn lower_get_state_script(&mut self, binding_name: &str) {
1745 let state_props: Vec<ANFProperty> = self
1746 .properties
1747 .iter()
1748 .filter(|p| !p.readonly)
1749 .cloned()
1750 .collect();
1751
1752 if state_props.is_empty() {
1753 self.emit_op(StackOp::Push(PushValue::Bytes(Vec::new())));
1754 self.sm.push(binding_name);
1755 return;
1756 }
1757
1758 let mut first = true;
1759 for prop in &state_props {
1760 if self.sm.has(&prop.name) {
1761 self.bring_to_top(&prop.name, true); } else if let Some(ref val) = prop.initial_value {
1763 self.push_json_value(val);
1764 self.sm.push("");
1765 } else {
1766 self.emit_op(StackOp::Push(PushValue::Int(0)));
1767 self.sm.push("");
1768 }
1769
1770 if prop.prop_type == "bigint" {
1772 self.emit_op(StackOp::Push(PushValue::Int(8)));
1773 self.sm.push("");
1774 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1775 self.sm.pop(); } else if prop.prop_type == "boolean" {
1777 self.emit_op(StackOp::Push(PushValue::Int(1)));
1778 self.sm.push("");
1779 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
1780 self.sm.pop(); } else if prop.prop_type == "ByteString" {
1782 self.emit_push_data_encode();
1784 }
1785 if !first {
1788 self.sm.pop();
1789 self.sm.pop();
1790 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
1791 self.sm.push("");
1792 }
1793 first = false;
1794 }
1795
1796 self.sm.pop();
1797 self.sm.push(binding_name);
1798 self.track_depth();
1799 }
1800
1801 fn lower_compute_state_output_hash(
1805 &mut self,
1806 binding_name: &str,
1807 args: &[String],
1808 binding_index: usize,
1809 last_uses: &std::collections::HashMap<String, usize>,
1810 ) {
1811 let preimage_ref = &args[0];
1812 let state_bytes_ref = &args[1];
1813
1814 let sb_last = self.is_last_use(state_bytes_ref, binding_index, last_uses);
1816 self.bring_to_top(state_bytes_ref, sb_last);
1817
1818 let pre_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1820 self.bring_to_top(preimage_ref, pre_last);
1821
1822 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1824 self.sm.push("");
1825 self.emit_op(StackOp::Push(PushValue::Int(52))); self.sm.push("");
1827 self.emit_op(StackOp::Opcode("OP_SUB".into()));
1828 self.sm.pop();
1829 self.sm.pop();
1830 self.sm.push("");
1831 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
1833 self.sm.pop();
1834 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip); self.sm.pop();
1838 self.sm.pop();
1839 self.sm.push("");
1840 self.emit_op(StackOp::Push(PushValue::Int(8)));
1841 self.sm.push("");
1842 self.emit_op(StackOp::Opcode("OP_SPLIT".into())); self.sm.pop();
1844 self.sm.pop();
1845 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop); self.sm.pop();
1849 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
1853 self.sm.pop();
1854
1855 self.bring_to_top("_codePart", false);
1857 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1861 self.sm.push("");
1862 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1863 self.sm.pop();
1864 self.sm.pop();
1865 self.sm.push("");
1866 self.emit_op(StackOp::Swap);
1869 self.sm.swap();
1870 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1871 self.sm.pop();
1872 self.sm.pop();
1873 self.sm.push("");
1874 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1878 self.sm.push("");
1879 self.emit_varint_encoding();
1880
1881 self.emit_op(StackOp::Swap);
1883 self.sm.swap();
1884 self.sm.pop();
1885 self.sm.pop();
1886 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1887 self.sm.push("");
1888
1889 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
1891 self.sm.push("");
1892 self.emit_op(StackOp::Swap);
1893 self.sm.swap();
1894 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1895 self.sm.pop();
1896 self.sm.pop();
1897 self.sm.push("");
1898
1899 self.emit_op(StackOp::Opcode("OP_HASH256".into()));
1901
1902 self.sm.pop();
1903 self.sm.push(binding_name);
1904 self.track_depth();
1905 }
1906
1907 fn lower_compute_state_output(
1911 &mut self,
1912 binding_name: &str,
1913 args: &[String],
1914 binding_index: usize,
1915 last_uses: &std::collections::HashMap<String, usize>,
1916 ) {
1917 let preimage_ref = &args[0];
1918 let state_bytes_ref = &args[1];
1919 let new_amount_ref = &args[2];
1920
1921 let pre_last = self.is_last_use(preimage_ref, binding_index, last_uses);
1923 self.bring_to_top(preimage_ref, pre_last);
1924 self.emit_op(StackOp::Drop);
1925 self.sm.pop();
1926
1927 let amount_last = self.is_last_use(new_amount_ref, binding_index, last_uses);
1929 self.bring_to_top(new_amount_ref, amount_last);
1930 self.emit_op(StackOp::Push(PushValue::Int(8)));
1931 self.sm.push("");
1932 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
1933 self.sm.pop();
1934 self.sm.pop();
1935 self.sm.push("");
1936 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
1937 self.sm.pop();
1938
1939 let sb_last = self.is_last_use(state_bytes_ref, binding_index, last_uses);
1941 self.bring_to_top(state_bytes_ref, sb_last);
1942
1943 self.bring_to_top("_codePart", false);
1945 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
1949 self.sm.push("");
1950 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1951 self.sm.pop();
1952 self.sm.pop();
1953 self.sm.push("");
1954 self.emit_op(StackOp::Swap);
1957 self.sm.swap();
1958 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1959 self.sm.pop();
1960 self.sm.pop();
1961 self.sm.push("");
1962 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
1966 self.sm.push("");
1967 self.emit_varint_encoding();
1968
1969 self.emit_op(StackOp::Swap);
1971 self.sm.swap();
1972 self.sm.pop();
1973 self.sm.pop();
1974 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1975 self.sm.push("");
1976
1977 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
1979 self.sm.push("");
1980 self.emit_op(StackOp::Swap);
1981 self.sm.swap();
1982 self.emit_op(StackOp::Opcode("OP_CAT".into()));
1983 self.sm.pop();
1984 self.sm.pop();
1985 self.sm.push("");
1986 self.sm.pop();
1989 self.sm.push(binding_name);
1990 self.track_depth();
1991 }
1992
1993 fn lower_build_change_output(
1997 &mut self,
1998 binding_name: &str,
1999 args: &[String],
2000 binding_index: usize,
2001 last_uses: &std::collections::HashMap<String, usize>,
2002 ) {
2003 let pkh_ref = &args[0];
2004 let amount_ref = &args[1];
2005
2006 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x19, 0x76, 0xa9, 0x14])));
2009 self.sm.push("");
2010
2011 let pkh_last = self.is_last_use(pkh_ref, binding_index, last_uses);
2013 self.bring_to_top(pkh_ref, pkh_last);
2014 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2016 self.sm.pop();
2017 self.sm.pop();
2018 self.sm.push("");
2019
2020 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x88, 0xac])));
2022 self.sm.push("");
2023 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2025 self.sm.pop();
2026 self.sm.pop();
2027 self.sm.push("");
2028 let amount_last = self.is_last_use(amount_ref, binding_index, last_uses);
2032 self.bring_to_top(amount_ref, amount_last);
2033 self.emit_op(StackOp::Push(PushValue::Int(8)));
2034 self.sm.push("");
2035 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
2036 self.sm.pop(); self.emit_op(StackOp::Swap);
2039 self.sm.swap();
2040 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2042 self.sm.pop();
2043 self.sm.pop();
2044 self.sm.push("");
2045 self.sm.pop();
2048 self.sm.push(binding_name);
2049 self.track_depth();
2050 }
2051
2052 fn lower_add_output(
2053 &mut self,
2054 binding_name: &str,
2055 satoshis: &str,
2056 state_values: &[String],
2057 _preimage: &str,
2058 binding_index: usize,
2059 last_uses: &HashMap<String, usize>,
2060 ) {
2061 let state_props: Vec<ANFProperty> = self
2067 .properties
2068 .iter()
2069 .filter(|p| !p.readonly)
2070 .cloned()
2071 .collect();
2072
2073 self.bring_to_top("_codePart", false);
2075 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0x6a])));
2079 self.sm.push("");
2080 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2081 self.sm.pop();
2082 self.sm.pop();
2083 self.sm.push("");
2084 for (i, value_ref) in state_values.iter().enumerate() {
2088 if i >= state_props.len() {
2089 break;
2090 }
2091 let prop = &state_props[i];
2092
2093 let is_last = self.is_last_use(value_ref, binding_index, last_uses);
2094 self.bring_to_top(value_ref, is_last);
2095
2096 if prop.prop_type == "bigint" {
2097 self.emit_op(StackOp::Push(PushValue::Int(8)));
2098 self.sm.push("");
2099 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
2100 self.sm.pop();
2101 } else if prop.prop_type == "boolean" {
2102 self.emit_op(StackOp::Push(PushValue::Int(1)));
2103 self.sm.push("");
2104 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
2105 self.sm.pop();
2106 } else if prop.prop_type == "ByteString" {
2107 self.emit_push_data_encode();
2109 }
2110
2111 self.sm.pop();
2112 self.sm.pop();
2113 self.emit_op(StackOp::Opcode("OP_CAT".to_string()));
2114 self.sm.push("");
2115 }
2116 self.emit_op(StackOp::Opcode("OP_SIZE".into())); self.sm.push("");
2121 self.emit_varint_encoding();
2122 self.emit_op(StackOp::Swap);
2126 self.sm.swap();
2127 self.sm.pop();
2128 self.sm.pop();
2129 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2130 self.sm.push("");
2131 let is_last_satoshis = self.is_last_use(satoshis, binding_index, last_uses);
2135 self.bring_to_top(satoshis, is_last_satoshis);
2136 self.emit_op(StackOp::Push(PushValue::Int(8)));
2137 self.sm.push("");
2138 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
2139 self.sm.pop(); self.emit_op(StackOp::Swap);
2142 self.sm.swap();
2143 self.sm.pop();
2144 self.sm.pop();
2145 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
2147 self.sm.pop();
2151 self.sm.push(binding_name);
2152 self.track_depth();
2153 }
2154
2155 fn lower_add_raw_output(
2159 &mut self,
2160 binding_name: &str,
2161 satoshis: &str,
2162 script_bytes: &str,
2163 binding_index: usize,
2164 last_uses: &HashMap<String, usize>,
2165 ) {
2166 let script_is_last = self.is_last_use(script_bytes, binding_index, last_uses);
2168 self.bring_to_top(script_bytes, script_is_last);
2169
2170 self.emit_op(StackOp::Opcode("OP_SIZE".to_string())); self.sm.push("");
2173 self.emit_varint_encoding();
2174 self.emit_op(StackOp::Swap); self.sm.swap();
2179 self.sm.pop();
2180 self.sm.pop();
2181 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
2183
2184 let sat_is_last = self.is_last_use(satoshis, binding_index, last_uses);
2186 self.bring_to_top(satoshis, sat_is_last);
2187 self.emit_op(StackOp::Push(PushValue::Int(8)));
2188 self.sm.push("");
2189 self.emit_op(StackOp::Opcode("OP_NUM2BIN".to_string()));
2190 self.sm.pop(); self.emit_op(StackOp::Swap);
2193 self.sm.swap();
2194 self.sm.pop();
2195 self.sm.pop();
2196 self.emit_op(StackOp::Opcode("OP_CAT".to_string())); self.sm.push("");
2198
2199 self.sm.pop();
2201 self.sm.push(binding_name);
2202 self.track_depth();
2203 }
2204
2205 fn lower_array_literal(
2206 &mut self,
2207 binding_name: &str,
2208 elements: &[String],
2209 binding_index: usize,
2210 last_uses: &HashMap<String, usize>,
2211 ) {
2212 for elem in elements {
2216 let is_last = self.is_last_use(elem, binding_index, last_uses);
2217 self.bring_to_top(elem, is_last);
2218 self.sm.pop();
2219 self.sm.push(""); }
2221 if !elements.is_empty() {
2223 self.sm.pop();
2224 }
2225 self.sm.push(binding_name);
2226 self.track_depth();
2227 }
2228
2229 fn lower_check_multi_sig(
2230 &mut self,
2231 binding_name: &str,
2232 args: &[String],
2233 binding_index: usize,
2234 last_uses: &HashMap<String, usize>,
2235 ) {
2236 self.emit_op(StackOp::Push(PushValue::Int(0)));
2245 self.sm.push("");
2246
2247 let sigs_is_last = self.is_last_use(&args[0], binding_index, last_uses);
2249 self.bring_to_top(&args[0], sigs_is_last);
2250
2251 let pks_is_last = self.is_last_use(&args[1], binding_index, last_uses);
2253 self.bring_to_top(&args[1], pks_is_last);
2254
2255 self.sm.pop(); self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_CHECKMULTISIG".to_string()));
2261 self.sm.push(binding_name);
2262 self.track_depth();
2263 }
2264
2265 fn lower_check_preimage(
2266 &mut self,
2267 binding_name: &str,
2268 preimage: &str,
2269 binding_index: usize,
2270 last_uses: &HashMap<String, usize>,
2271 ) {
2272 self.emit_op(StackOp::Opcode("OP_CODESEPARATOR".to_string()));
2308
2309 let is_last = self.is_last_use(preimage, binding_index, last_uses);
2311 self.bring_to_top(preimage, is_last);
2312
2313 self.bring_to_top("_opPushTxSig", true);
2315
2316 let g: Vec<u8> = vec![
2318 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB,
2319 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B,
2320 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28,
2321 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17,
2322 0x98,
2323 ];
2324 self.emit_op(StackOp::Push(PushValue::Bytes(g)));
2325 self.sm.push(""); self.emit_op(StackOp::Opcode("OP_CHECKSIG".to_string()));
2329 self.sm.pop(); self.sm.pop(); self.sm.push(""); self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
2335 self.sm.pop(); self.sm.pop();
2340 self.sm.push(binding_name);
2341
2342 self.track_depth();
2343 }
2344
2345 fn lower_deserialize_state(
2354 &mut self,
2355 preimage_ref: &str,
2356 binding_index: usize,
2357 last_uses: &HashMap<String, usize>,
2358 ) {
2359 let mut prop_names: Vec<String> = Vec::new();
2360 let mut prop_types: Vec<String> = Vec::new();
2361 let mut prop_sizes: Vec<i128> = Vec::new();
2362 let mut has_variable_length = false;
2363
2364 for p in &self.properties {
2365 if p.readonly {
2366 continue;
2367 }
2368 prop_names.push(p.name.clone());
2369 prop_types.push(p.prop_type.clone());
2370 let sz: i128 = match p.prop_type.as_str() {
2371 "bigint" => 8,
2372 "boolean" => 1,
2373 "PubKey" => 33,
2374 "Addr" => 20,
2375 "Sha256" => 32,
2376 "Point" => 64,
2377 "ByteString" => { has_variable_length = true; -1 },
2378 _ => panic!("deserialize_state: unsupported type: {}", p.prop_type),
2379 };
2380 prop_sizes.push(sz);
2381 }
2382
2383 if prop_names.is_empty() {
2384 return;
2385 }
2386
2387 let is_last = self.is_last_use(preimage_ref, binding_index, last_uses);
2388 self.bring_to_top(preimage_ref, is_last);
2389
2390 self.emit_op(StackOp::Push(PushValue::Int(104)));
2392 self.sm.push("");
2393 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2394 self.sm.pop(); self.sm.pop();
2395 self.sm.push(""); self.sm.push("");
2396 self.emit_op(StackOp::Nip);
2397 self.sm.pop(); self.sm.pop();
2398 self.sm.push("");
2399
2400 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2402 self.sm.push("");
2403 self.emit_op(StackOp::Push(PushValue::Int(44)));
2404 self.sm.push("");
2405 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2406 self.sm.pop(); self.sm.pop();
2407 self.sm.push("");
2408 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2409 self.sm.pop(); self.sm.pop();
2410 self.sm.push(""); self.sm.push("");
2411 self.emit_op(StackOp::Drop);
2412 self.sm.pop();
2413
2414 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2416 self.sm.push("");
2417 self.emit_op(StackOp::Push(PushValue::Int(8)));
2418 self.sm.push("");
2419 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2420 self.sm.pop(); self.sm.pop();
2421 self.sm.push("");
2422 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2423 self.sm.pop(); self.sm.pop();
2424 self.sm.push(""); self.sm.push("");
2425 self.emit_op(StackOp::Drop);
2426 self.sm.pop();
2427
2428 if !has_variable_length {
2429 let state_len: i128 = prop_sizes.iter().sum();
2430
2431 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2433 self.sm.push("");
2434 self.emit_op(StackOp::Push(PushValue::Int(state_len)));
2435 self.sm.push("");
2436 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2437 self.sm.pop(); self.sm.pop();
2438 self.sm.push("");
2439 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2440 self.sm.pop(); self.sm.pop();
2441 self.sm.push(""); self.sm.push("");
2442 self.emit_op(StackOp::Nip);
2443 self.sm.pop(); self.sm.pop();
2444 self.sm.push("");
2445
2446 self.split_fixed_state_fields(&prop_names, &prop_types, &prop_sizes);
2448 } else if !self.sm.has("_codePart") {
2449 self.emit_op(StackOp::Drop);
2451 self.sm.pop();
2452 } else {
2453 self.emit_op(StackOp::Push(PushValue::Int(1)));
2455 self.sm.push("");
2456 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2457 self.sm.pop(); self.sm.pop();
2458 self.sm.push(""); self.sm.push("");
2459 self.emit_op(StackOp::Swap);
2460 self.sm.swap();
2461 self.emit_op(StackOp::Dup);
2462 self.sm.push("");
2463 self.emit_op(StackOp::Push(PushValue::Bytes(vec![0])));
2465 self.sm.push("");
2466 self.emit_op(StackOp::Opcode("OP_CAT".into()));
2467 self.sm.pop(); self.sm.pop();
2468 self.sm.push("");
2469 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
2470 self.emit_op(StackOp::Push(PushValue::Int(253)));
2471 self.sm.push("");
2472 self.emit_op(StackOp::Opcode("OP_LESSTHAN".into()));
2473 self.sm.pop(); self.sm.pop();
2474 self.sm.push("");
2475
2476 self.emit_op(StackOp::Opcode("OP_IF".into()));
2477 self.sm.pop();
2478 let sm_at_varint_if = self.sm.clone();
2479 self.emit_op(StackOp::Drop);
2480 self.sm.pop();
2481
2482 self.emit_op(StackOp::Opcode("OP_ELSE".into()));
2483 self.sm = sm_at_varint_if.clone();
2484 self.emit_op(StackOp::Drop);
2485 self.sm.pop();
2486 self.emit_op(StackOp::Push(PushValue::Int(2)));
2487 self.sm.push("");
2488 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2489 self.sm.pop(); self.sm.pop();
2490 self.sm.push(""); self.sm.push("");
2491 self.emit_op(StackOp::Nip);
2492 self.sm.pop(); self.sm.pop();
2493 self.sm.push("");
2494
2495 self.emit_op(StackOp::Opcode("OP_ENDIF".into()));
2496
2497 self.bring_to_top("_codePart", false);
2499 self.emit_op(StackOp::Opcode("OP_SIZE".into()));
2500 self.sm.push("");
2501 self.emit_op(StackOp::Nip);
2502 self.sm.pop(); self.sm.pop();
2503 self.sm.push("");
2504 self.emit_op(StackOp::PushCodeSepIndex);
2505 self.sm.push("");
2506 self.emit_op(StackOp::Opcode("OP_SUB".into()));
2507 self.sm.pop(); self.sm.pop();
2508 self.sm.push("");
2509
2510 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2512 self.sm.pop(); self.sm.pop();
2513 self.sm.push(""); self.sm.push("");
2514 self.emit_op(StackOp::Nip);
2515 self.sm.pop(); self.sm.pop();
2516 self.sm.push("");
2517
2518 self.parse_variable_length_state_fields(&prop_names, &prop_types, &prop_sizes);
2520 }
2521
2522 self.track_depth();
2523 }
2524
2525 fn split_fixed_state_fields(
2526 &mut self,
2527 prop_names: &[String],
2528 prop_types: &[String],
2529 prop_sizes: &[i128],
2530 ) {
2531 let num_props = prop_names.len();
2532 if num_props == 1 {
2533 if prop_types[0] == "bigint" || prop_types[0] == "boolean" {
2534 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2535 }
2536 self.sm.pop();
2537 self.sm.push(&prop_names[0]);
2538 } else {
2539 for i in 0..num_props {
2540 let sz = prop_sizes[i];
2541 if i < num_props - 1 {
2542 self.emit_op(StackOp::Push(PushValue::Int(sz)));
2543 self.sm.push("");
2544 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2545 self.sm.pop(); self.sm.pop();
2546 self.sm.push(""); self.sm.push("");
2547 self.emit_op(StackOp::Swap);
2548 self.sm.swap();
2549 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2550 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2551 }
2552 self.emit_op(StackOp::Swap);
2553 self.sm.swap();
2554 self.sm.pop(); self.sm.pop();
2555 self.sm.push(&prop_names[i]);
2556 self.sm.push("");
2557 } else {
2558 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2559 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2560 }
2561 self.sm.pop();
2562 self.sm.push(&prop_names[i]);
2563 }
2564 }
2565 }
2566 }
2567
2568 fn parse_variable_length_state_fields(
2569 &mut self,
2570 prop_names: &[String],
2571 prop_types: &[String],
2572 prop_sizes: &[i128],
2573 ) {
2574 let num_props = prop_names.len();
2575 if num_props == 1 {
2576 if prop_types[0] == "ByteString" {
2577 self.emit_push_data_decode(); self.emit_op(StackOp::Drop); self.sm.pop();
2580 } else if prop_types[0] == "bigint" || prop_types[0] == "boolean" {
2581 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
2582 }
2583 self.sm.pop();
2584 self.sm.push(&prop_names[0]);
2585 } else {
2586 for i in 0..num_props {
2587 if i < num_props - 1 {
2588 if prop_types[i] == "ByteString" {
2589 self.emit_push_data_decode(); self.sm.pop(); self.sm.pop();
2592 self.sm.push(&prop_names[i]);
2593 self.sm.push(""); } else {
2595 self.emit_op(StackOp::Push(PushValue::Int(prop_sizes[i])));
2596 self.sm.push("");
2597 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
2598 self.sm.pop(); self.sm.pop();
2599 self.sm.push(""); self.sm.push("");
2600 self.emit_op(StackOp::Swap); self.sm.swap();
2601 if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2602 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
2603 }
2604 self.emit_op(StackOp::Swap); self.sm.swap();
2605 self.sm.pop(); self.sm.pop();
2606 self.sm.push(&prop_names[i]);
2607 self.sm.push("");
2608 }
2609 } else {
2610 if prop_types[i] == "ByteString" {
2611 self.emit_push_data_decode(); self.emit_op(StackOp::Drop); self.sm.pop();
2614 } else if prop_types[i] == "bigint" || prop_types[i] == "boolean" {
2615 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
2616 }
2617 self.sm.pop();
2618 self.sm.push(&prop_names[i]);
2619 }
2620 }
2621 }
2622 }
2623
2624 fn lower_extractor(
2642 &mut self,
2643 binding_name: &str,
2644 func_name: &str,
2645 args: &[String],
2646 binding_index: usize,
2647 last_uses: &HashMap<String, usize>,
2648 ) {
2649 assert!(!args.is_empty(), "{} requires 1 argument", func_name);
2650 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
2651 self.bring_to_top(&args[0], is_last);
2652
2653 self.sm.pop(); match func_name {
2657 "extractVersion" => {
2658 self.emit_op(StackOp::Push(PushValue::Int(4)));
2660 self.sm.push("");
2661 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2662 self.sm.pop();
2663 self.sm.push("");
2664 self.sm.push("");
2665 self.emit_op(StackOp::Drop);
2666 self.sm.pop();
2667 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2668 }
2669 "extractHashPrevouts" => {
2670 self.emit_op(StackOp::Push(PushValue::Int(4)));
2672 self.sm.push("");
2673 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2674 self.sm.pop();
2675 self.sm.push("");
2676 self.sm.push("");
2677 self.emit_op(StackOp::Nip);
2678 self.sm.pop();
2679 self.sm.pop();
2680 self.sm.push("");
2681 self.emit_op(StackOp::Push(PushValue::Int(32)));
2682 self.sm.push("");
2683 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2684 self.sm.pop(); self.sm.pop(); self.sm.push("");
2687 self.sm.push("");
2688 self.emit_op(StackOp::Drop);
2689 self.sm.pop();
2690 }
2691 "extractHashSequence" => {
2692 self.emit_op(StackOp::Push(PushValue::Int(36)));
2694 self.sm.push("");
2695 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2696 self.sm.pop();
2697 self.sm.push("");
2698 self.sm.push("");
2699 self.emit_op(StackOp::Nip);
2700 self.sm.pop();
2701 self.sm.pop();
2702 self.sm.push("");
2703 self.emit_op(StackOp::Push(PushValue::Int(32)));
2704 self.sm.push("");
2705 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2706 self.sm.pop(); self.sm.pop(); self.sm.push("");
2709 self.sm.push("");
2710 self.emit_op(StackOp::Drop);
2711 self.sm.pop();
2712 }
2713 "extractOutpoint" => {
2714 self.emit_op(StackOp::Push(PushValue::Int(68)));
2716 self.sm.push("");
2717 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2718 self.sm.pop();
2719 self.sm.push("");
2720 self.sm.push("");
2721 self.emit_op(StackOp::Nip);
2722 self.sm.pop();
2723 self.sm.pop();
2724 self.sm.push("");
2725 self.emit_op(StackOp::Push(PushValue::Int(36)));
2726 self.sm.push("");
2727 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2728 self.sm.pop(); self.sm.pop(); self.sm.push("");
2731 self.sm.push("");
2732 self.emit_op(StackOp::Drop);
2733 self.sm.pop();
2734 }
2735 "extractSigHashType" => {
2736 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2739 self.sm.push("");
2740 self.sm.push("");
2741 self.emit_op(StackOp::Push(PushValue::Int(4)));
2742 self.sm.push("");
2743 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2744 self.sm.pop();
2745 self.sm.pop();
2746 self.sm.push("");
2747 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2748 self.sm.pop();
2749 self.sm.pop();
2750 self.sm.push("");
2751 self.sm.push("");
2752 self.emit_op(StackOp::Nip);
2753 self.sm.pop();
2754 self.sm.pop();
2755 self.sm.push("");
2756 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2757 }
2758 "extractLocktime" => {
2759 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2762 self.sm.push("");
2763 self.sm.push("");
2764 self.emit_op(StackOp::Push(PushValue::Int(8)));
2765 self.sm.push("");
2766 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2767 self.sm.pop();
2768 self.sm.pop();
2769 self.sm.push("");
2770 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2771 self.sm.pop();
2772 self.sm.pop();
2773 self.sm.push("");
2774 self.sm.push("");
2775 self.emit_op(StackOp::Nip);
2776 self.sm.pop();
2777 self.sm.pop();
2778 self.sm.push("");
2779 self.emit_op(StackOp::Push(PushValue::Int(4)));
2780 self.sm.push("");
2781 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2782 self.sm.pop(); self.sm.pop(); self.sm.push("");
2785 self.sm.push("");
2786 self.emit_op(StackOp::Drop);
2787 self.sm.pop();
2788 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2789 }
2790 "extractOutputHash" | "extractOutputs" => {
2791 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2794 self.sm.push("");
2795 self.sm.push("");
2796 self.emit_op(StackOp::Push(PushValue::Int(40)));
2797 self.sm.push("");
2798 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2799 self.sm.pop();
2800 self.sm.pop();
2801 self.sm.push("");
2802 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2803 self.sm.pop();
2804 self.sm.pop();
2805 self.sm.push("");
2806 self.sm.push("");
2807 self.emit_op(StackOp::Nip);
2808 self.sm.pop();
2809 self.sm.pop();
2810 self.sm.push("");
2811 self.emit_op(StackOp::Push(PushValue::Int(32)));
2812 self.sm.push("");
2813 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2814 self.sm.pop(); self.sm.pop(); self.sm.push("");
2817 self.sm.push("");
2818 self.emit_op(StackOp::Drop);
2819 self.sm.pop();
2820 }
2821 "extractAmount" => {
2822 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2825 self.sm.push("");
2826 self.sm.push("");
2827 self.emit_op(StackOp::Push(PushValue::Int(52)));
2828 self.sm.push("");
2829 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2830 self.sm.pop();
2831 self.sm.pop();
2832 self.sm.push("");
2833 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2834 self.sm.pop();
2835 self.sm.pop();
2836 self.sm.push("");
2837 self.sm.push("");
2838 self.emit_op(StackOp::Nip);
2839 self.sm.pop();
2840 self.sm.pop();
2841 self.sm.push("");
2842 self.emit_op(StackOp::Push(PushValue::Int(8)));
2843 self.sm.push("");
2844 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2845 self.sm.pop(); self.sm.pop(); self.sm.push("");
2848 self.sm.push("");
2849 self.emit_op(StackOp::Drop);
2850 self.sm.pop();
2851 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2852 }
2853 "extractSequence" => {
2854 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2857 self.sm.push("");
2858 self.sm.push("");
2859 self.emit_op(StackOp::Push(PushValue::Int(44)));
2860 self.sm.push("");
2861 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2862 self.sm.pop();
2863 self.sm.pop();
2864 self.sm.push("");
2865 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2866 self.sm.pop();
2867 self.sm.pop();
2868 self.sm.push("");
2869 self.sm.push("");
2870 self.emit_op(StackOp::Nip);
2871 self.sm.pop();
2872 self.sm.pop();
2873 self.sm.push("");
2874 self.emit_op(StackOp::Push(PushValue::Int(4)));
2875 self.sm.push("");
2876 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2877 self.sm.pop(); self.sm.pop(); self.sm.push("");
2880 self.sm.push("");
2881 self.emit_op(StackOp::Drop);
2882 self.sm.pop();
2883 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2884 }
2885 "extractScriptCode" => {
2886 self.emit_op(StackOp::Push(PushValue::Int(104)));
2889 self.sm.push("");
2890 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2891 self.sm.pop();
2892 self.sm.push("");
2893 self.sm.push("");
2894 self.emit_op(StackOp::Nip);
2895 self.sm.pop();
2896 self.sm.pop();
2897 self.sm.push("");
2898 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
2899 self.sm.push("");
2900 self.emit_op(StackOp::Push(PushValue::Int(52)));
2901 self.sm.push("");
2902 self.emit_op(StackOp::Opcode("OP_SUB".to_string()));
2903 self.sm.pop();
2904 self.sm.pop();
2905 self.sm.push("");
2906 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2907 self.sm.pop();
2908 self.sm.pop();
2909 self.sm.push("");
2910 self.sm.push("");
2911 self.emit_op(StackOp::Drop);
2912 self.sm.pop();
2913 }
2914 "extractInputIndex" => {
2915 self.emit_op(StackOp::Push(PushValue::Int(100)));
2918 self.sm.push("");
2919 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2920 self.sm.pop();
2921 self.sm.push("");
2922 self.sm.push("");
2923 self.emit_op(StackOp::Nip);
2924 self.sm.pop();
2925 self.sm.pop();
2926 self.sm.push("");
2927 self.emit_op(StackOp::Push(PushValue::Int(4)));
2928 self.sm.push("");
2929 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2930 self.sm.pop(); self.sm.pop(); self.sm.push("");
2933 self.sm.push("");
2934 self.emit_op(StackOp::Drop);
2935 self.sm.pop();
2936 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
2937 }
2938 _ => panic!("unknown extractor: {}", func_name),
2939 }
2940
2941 self.sm.pop();
2943 self.sm.push(binding_name);
2944 self.track_depth();
2945 }
2946
2947 fn lower_array_access(
2961 &mut self,
2962 binding_name: &str,
2963 args: &[String],
2964 binding_index: usize,
2965 last_uses: &HashMap<String, usize>,
2966 ) {
2967 assert!(args.len() >= 2, "__array_access requires 2 arguments (object, index)");
2968
2969 let obj = &args[0];
2970 let index = &args[1];
2971
2972 let obj_is_last = self.is_last_use(obj, binding_index, last_uses);
2974 self.bring_to_top(obj, obj_is_last);
2975
2976 let index_is_last = self.is_last_use(index, binding_index, last_uses);
2978 self.bring_to_top(index, index_is_last);
2979
2980 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
2984 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Nip);
2989 self.sm.pop();
2990 let right_part = self.sm.pop();
2991 self.sm.push(&right_part);
2992
2993 self.emit_op(StackOp::Push(PushValue::Int(1)));
2995 self.sm.push("");
2996
2997 self.sm.pop(); self.sm.pop(); self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
3001 self.sm.push(""); self.sm.push(""); self.emit_op(StackOp::Drop);
3006 self.sm.pop();
3007 self.sm.pop();
3008 self.sm.push("");
3009
3010 self.sm.pop();
3012 self.emit_op(StackOp::Opcode("OP_BIN2NUM".to_string()));
3013
3014 self.sm.push(binding_name);
3015 self.track_depth();
3016 }
3017
3018 fn lower_reverse_bytes(
3019 &mut self,
3020 binding_name: &str,
3021 args: &[String],
3022 binding_index: usize,
3023 last_uses: &HashMap<String, usize>,
3024 ) {
3025 assert!(!args.is_empty(), "reverseBytes requires 1 argument");
3026 let is_last = self.is_last_use(&args[0], binding_index, last_uses);
3027 self.bring_to_top(&args[0], is_last);
3028
3029 self.sm.pop();
3033
3034 self.emit_op(StackOp::Push(PushValue::Int(0)));
3036 self.emit_op(StackOp::Swap);
3037
3038 for _ in 0..520 {
3040 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3042 self.emit_op(StackOp::Opcode("OP_SIZE".to_string()));
3043 self.emit_op(StackOp::Nip);
3044 self.emit_op(StackOp::If {
3045 then_ops: vec![
3046 StackOp::Push(PushValue::Int(1)),
3047 StackOp::Opcode("OP_SPLIT".to_string()),
3048 StackOp::Swap,
3049 StackOp::Rot,
3050 StackOp::Opcode("OP_CAT".to_string()),
3051 StackOp::Swap,
3052 ],
3053 else_ops: vec![],
3054 });
3055 }
3056
3057 self.emit_op(StackOp::Drop);
3059
3060 self.sm.push(binding_name);
3061 self.track_depth();
3062 }
3063
3064 fn lower_substr(
3065 &mut self,
3066 binding_name: &str,
3067 args: &[String],
3068 binding_index: usize,
3069 last_uses: &HashMap<String, usize>,
3070 ) {
3071 assert!(args.len() >= 3, "substr requires 3 arguments");
3072
3073 let data = &args[0];
3074 let start = &args[1];
3075 let length = &args[2];
3076
3077 let data_is_last = self.is_last_use(data, binding_index, last_uses);
3078 self.bring_to_top(data, data_is_last);
3079
3080 let start_is_last = self.is_last_use(start, binding_index, last_uses);
3081 self.bring_to_top(start, start_is_last);
3082
3083 self.sm.pop();
3084 self.sm.pop();
3085 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
3086 self.sm.push("");
3087 self.sm.push("");
3088
3089 self.emit_op(StackOp::Nip);
3090 self.sm.pop();
3091 let right_part = self.sm.pop();
3092 self.sm.push(&right_part);
3093
3094 let len_is_last = self.is_last_use(length, binding_index, last_uses);
3095 self.bring_to_top(length, len_is_last);
3096
3097 self.sm.pop();
3098 self.sm.pop();
3099 self.emit_op(StackOp::Opcode("OP_SPLIT".to_string()));
3100 self.sm.push("");
3101 self.sm.push("");
3102
3103 self.emit_op(StackOp::Drop);
3104 self.sm.pop();
3105 self.sm.pop();
3106
3107 self.sm.push(binding_name);
3108 self.track_depth();
3109 }
3110 fn lower_verify_rabin_sig(
3111 &mut self,
3112 binding_name: &str,
3113 args: &[String],
3114 binding_index: usize,
3115 last_uses: &HashMap<String, usize>,
3116 ) {
3117 assert!(args.len() >= 4, "verifyRabinSig requires 4 arguments");
3118
3119 let msg = &args[0];
3123 let sig = &args[1];
3124 let padding = &args[2];
3125 let pub_key = &args[3];
3126
3127 let msg_is_last = self.is_last_use(msg, binding_index, last_uses);
3128 self.bring_to_top(msg, msg_is_last);
3129
3130 let sig_is_last = self.is_last_use(sig, binding_index, last_uses);
3131 self.bring_to_top(sig, sig_is_last);
3132
3133 let padding_is_last = self.is_last_use(padding, binding_index, last_uses);
3134 self.bring_to_top(padding, padding_is_last);
3135
3136 let pub_key_is_last = self.is_last_use(pub_key, binding_index, last_uses);
3137 self.bring_to_top(pub_key, pub_key_is_last);
3138
3139 self.sm.pop();
3141 self.sm.pop();
3142 self.sm.pop();
3143 self.sm.pop();
3144
3145 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()));
3150 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()));
3156 self.emit_op(StackOp::Opcode("OP_EQUAL".to_string()));
3157
3158 self.sm.push(binding_name);
3159 self.track_depth();
3160 }
3161
3162 fn lower_sign(
3165 &mut self,
3166 binding_name: &str,
3167 args: &[String],
3168 binding_index: usize,
3169 last_uses: &HashMap<String, usize>,
3170 ) {
3171 assert!(!args.is_empty(), "sign requires 1 argument");
3172 let x = &args[0];
3173
3174 let x_is_last = self.is_last_use(x, binding_index, last_uses);
3175 self.bring_to_top(x, x_is_last);
3176 self.sm.pop();
3177
3178 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3179 self.emit_op(StackOp::If {
3180 then_ops: vec![
3181 StackOp::Opcode("OP_DUP".to_string()),
3182 StackOp::Opcode("OP_ABS".to_string()),
3183 StackOp::Swap,
3184 StackOp::Opcode("OP_DIV".to_string()),
3185 ],
3186 else_ops: vec![],
3187 });
3188
3189 self.sm.push(binding_name);
3190 self.track_depth();
3191 }
3192
3193 fn lower_right(
3196 &mut self,
3197 binding_name: &str,
3198 args: &[String],
3199 binding_index: usize,
3200 last_uses: &HashMap<String, usize>,
3201 ) {
3202 assert!(args.len() >= 2, "right requires 2 arguments");
3203 let data = &args[0];
3204 let length = &args[1];
3205
3206 let data_is_last = self.is_last_use(data, binding_index, last_uses);
3207 self.bring_to_top(data, data_is_last);
3208
3209 let length_is_last = self.is_last_use(length, binding_index, last_uses);
3210 self.bring_to_top(length, length_is_last);
3211
3212 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);
3223 self.track_depth();
3224 }
3225
3226 fn emit_wots_one_chain(&mut self, chain_index: usize) {
3230 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3232 self.emit_op(StackOp::Push(PushValue::Int(15)));
3233 self.emit_op(StackOp::Swap);
3234 self.emit_op(StackOp::Opcode("OP_SUB".into()));
3235 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
3239 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
3241 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
3246 self.emit_op(StackOp::Push(PushValue::Int(32)));
3247 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
3248 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into())); self.emit_op(StackOp::Swap);
3250 for j in 0..15usize {
3256 let adrs_bytes = vec![chain_index as u8, j as u8];
3257 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3258 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".into()));
3259 self.emit_op(StackOp::If {
3260 then_ops: vec![
3261 StackOp::Opcode("OP_1SUB".into()), ],
3263 else_ops: vec![
3264 StackOp::Swap, StackOp::Push(PushValue::Int(2)),
3266 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, ],
3274 });
3275 }
3276 self.emit_op(StackOp::Drop); self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3281 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3282 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3283 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3284
3285 self.emit_op(StackOp::Rot);
3287 self.emit_op(StackOp::Opcode("OP_ADD".into()));
3288
3289 self.emit_op(StackOp::Swap);
3291 self.emit_op(StackOp::Push(PushValue::Int(3)));
3292 self.emit_op(StackOp::Opcode("OP_ROLL".into()));
3293 self.emit_op(StackOp::Opcode("OP_CAT".into()));
3294 }
3295
3296 fn lower_verify_wots(
3300 &mut self,
3301 binding_name: &str,
3302 args: &[String],
3303 binding_index: usize,
3304 last_uses: &HashMap<String, usize>,
3305 ) {
3306 assert!(args.len() >= 3, "verifyWOTS requires 3 arguments: msg, sig, pubkey");
3307
3308 for arg in args.iter() {
3309 let is_last = self.is_last_use(arg, binding_index, last_uses);
3310 self.bring_to_top(arg, is_last);
3311 }
3312 for _ in 0..3 { self.sm.pop(); }
3313 self.emit_op(StackOp::Push(PushValue::Int(32)));
3317 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);
3328 self.emit_op(StackOp::Push(PushValue::Int(0)));
3329 self.emit_op(StackOp::Opcode("OP_0".into()));
3330 self.emit_op(StackOp::Push(PushValue::Int(3)));
3331 self.emit_op(StackOp::Opcode("OP_ROLL".into()));
3332
3333 for byte_idx in 0..32 {
3335 if byte_idx < 31 {
3336 self.emit_op(StackOp::Push(PushValue::Int(1)));
3337 self.emit_op(StackOp::Opcode("OP_SPLIT".into()));
3338 self.emit_op(StackOp::Swap);
3339 }
3340 self.emit_op(StackOp::Push(PushValue::Int(0)));
3342 self.emit_op(StackOp::Push(PushValue::Int(1)));
3343 self.emit_op(StackOp::Opcode("OP_NUM2BIN".into()));
3344 self.emit_op(StackOp::Opcode("OP_CAT".into()));
3345 self.emit_op(StackOp::Opcode("OP_BIN2NUM".into()));
3346 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3348 self.emit_op(StackOp::Push(PushValue::Int(16)));
3349 self.emit_op(StackOp::Opcode("OP_DIV".into()));
3350 self.emit_op(StackOp::Swap);
3351 self.emit_op(StackOp::Push(PushValue::Int(16)));
3352 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3353
3354 if byte_idx < 31 {
3355 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3356 self.emit_op(StackOp::Swap);
3357 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3358 } else {
3359 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3360 }
3361
3362 self.emit_wots_one_chain(byte_idx * 2); if byte_idx < 31 {
3365 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3366 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3367 self.emit_op(StackOp::Swap);
3368 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3369 } else {
3370 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3371 }
3372
3373 self.emit_wots_one_chain(byte_idx * 2 + 1); if byte_idx < 31 {
3376 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3377 }
3378 }
3379
3380 self.emit_op(StackOp::Swap);
3382 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3384 self.emit_op(StackOp::Push(PushValue::Int(16)));
3385 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3386 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3387 self.emit_op(StackOp::Opcode("OP_DUP".into()));
3389 self.emit_op(StackOp::Push(PushValue::Int(16)));
3390 self.emit_op(StackOp::Opcode("OP_DIV".into()));
3391 self.emit_op(StackOp::Push(PushValue::Int(16)));
3392 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3393 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3394 self.emit_op(StackOp::Push(PushValue::Int(256)));
3396 self.emit_op(StackOp::Opcode("OP_DIV".into()));
3397 self.emit_op(StackOp::Push(PushValue::Int(16)));
3398 self.emit_op(StackOp::Opcode("OP_MOD".into()));
3399 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3400
3401 for ci in 0..3 {
3403 self.emit_op(StackOp::Opcode("OP_TOALTSTACK".into()));
3404 self.emit_op(StackOp::Push(PushValue::Int(0)));
3405 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3406 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into()));
3407 self.emit_wots_one_chain(64 + ci);
3408 self.emit_op(StackOp::Swap);
3409 self.emit_op(StackOp::Drop);
3410 }
3411
3412 self.emit_op(StackOp::Swap);
3414 self.emit_op(StackOp::Drop);
3415 self.emit_op(StackOp::Opcode("OP_SHA256".into()));
3417 self.emit_op(StackOp::Opcode("OP_FROMALTSTACK".into())); self.emit_op(StackOp::Opcode("OP_EQUAL".into()));
3419 self.emit_op(StackOp::Swap);
3421 self.emit_op(StackOp::Drop);
3422
3423 self.sm.push(binding_name);
3424 self.track_depth();
3425 }
3426
3427 fn lower_verify_slh_dsa(
3431 &mut self,
3432 binding_name: &str,
3433 param_key: &str,
3434 args: &[String],
3435 binding_index: usize,
3436 last_uses: &HashMap<String, usize>,
3437 ) {
3438 assert!(
3439 args.len() >= 3,
3440 "verifySLHDSA requires 3 arguments: msg, sig, pubkey"
3441 );
3442
3443 for arg in args.iter() {
3445 let is_last = self.is_last_use(arg, binding_index, last_uses);
3446 self.bring_to_top(arg, is_last);
3447 }
3448 for _ in 0..3 {
3449 self.sm.pop();
3450 }
3451
3452 super::slh_dsa::emit_verify_slh_dsa(&mut |op| self.ops.push(op), param_key);
3454
3455 self.sm.push(binding_name);
3456 self.track_depth();
3457 }
3458
3459 fn lower_sha256_compress(
3464 &mut self,
3465 binding_name: &str,
3466 args: &[String],
3467 binding_index: usize,
3468 last_uses: &HashMap<String, usize>,
3469 ) {
3470 assert!(
3471 args.len() >= 2,
3472 "sha256Compress requires 2 arguments: state, block"
3473 );
3474 for arg in args.iter() {
3475 let is_last = self.is_last_use(arg, binding_index, last_uses);
3476 self.bring_to_top(arg, is_last);
3477 }
3478 for _ in 0..2 {
3479 self.sm.pop();
3480 }
3481
3482 super::sha256::emit_sha256_compress(&mut |op| self.ops.push(op));
3483
3484 self.sm.push(binding_name);
3485 self.track_depth();
3486 }
3487
3488 fn lower_sha256_finalize(
3489 &mut self,
3490 binding_name: &str,
3491 args: &[String],
3492 binding_index: usize,
3493 last_uses: &HashMap<String, usize>,
3494 ) {
3495 assert!(
3496 args.len() >= 3,
3497 "sha256Finalize requires 3 arguments: state, remaining, msgBitLen"
3498 );
3499 for arg in args.iter() {
3500 let is_last = self.is_last_use(arg, binding_index, last_uses);
3501 self.bring_to_top(arg, is_last);
3502 }
3503 for _ in 0..3 {
3504 self.sm.pop();
3505 }
3506
3507 super::sha256::emit_sha256_finalize(&mut |op| self.ops.push(op));
3508
3509 self.sm.push(binding_name);
3510 self.track_depth();
3511 }
3512
3513 fn lower_blake3_compress(
3514 &mut self,
3515 binding_name: &str,
3516 args: &[String],
3517 binding_index: usize,
3518 last_uses: &HashMap<String, usize>,
3519 ) {
3520 assert!(
3521 args.len() >= 2,
3522 "blake3Compress requires 2 arguments: chainingValue, block"
3523 );
3524 for arg in args.iter() {
3525 let is_last = self.is_last_use(arg, binding_index, last_uses);
3526 self.bring_to_top(arg, is_last);
3527 }
3528 for _ in 0..2 {
3529 self.sm.pop();
3530 }
3531
3532 super::blake3::emit_blake3_compress(&mut |op| self.ops.push(op));
3533
3534 self.sm.push(binding_name);
3535 self.track_depth();
3536 }
3537
3538 fn lower_blake3_hash(
3539 &mut self,
3540 binding_name: &str,
3541 args: &[String],
3542 binding_index: usize,
3543 last_uses: &HashMap<String, usize>,
3544 ) {
3545 assert!(
3546 args.len() >= 1,
3547 "blake3Hash requires 1 argument: message"
3548 );
3549 for arg in args.iter() {
3550 let is_last = self.is_last_use(arg, binding_index, last_uses);
3551 self.bring_to_top(arg, is_last);
3552 }
3553 for _ in 0..1 {
3554 self.sm.pop();
3555 }
3556
3557 super::blake3::emit_blake3_hash(&mut |op| self.ops.push(op));
3558
3559 self.sm.push(binding_name);
3560 self.track_depth();
3561 }
3562
3563 fn lower_ec_builtin(
3564 &mut self,
3565 binding_name: &str,
3566 func_name: &str,
3567 args: &[String],
3568 binding_index: usize,
3569 last_uses: &HashMap<String, usize>,
3570 ) {
3571 for arg in args.iter() {
3573 let is_last = self.is_last_use(arg, binding_index, last_uses);
3574 self.bring_to_top(arg, is_last);
3575 }
3576 for _ in args {
3577 self.sm.pop();
3578 }
3579
3580 let emit = &mut |op: StackOp| self.ops.push(op);
3581
3582 match func_name {
3583 "ecAdd" => super::ec::emit_ec_add(emit),
3584 "ecMul" => super::ec::emit_ec_mul(emit),
3585 "ecMulGen" => super::ec::emit_ec_mul_gen(emit),
3586 "ecNegate" => super::ec::emit_ec_negate(emit),
3587 "ecOnCurve" => super::ec::emit_ec_on_curve(emit),
3588 "ecModReduce" => super::ec::emit_ec_mod_reduce(emit),
3589 "ecEncodeCompressed" => super::ec::emit_ec_encode_compressed(emit),
3590 "ecMakePoint" => super::ec::emit_ec_make_point(emit),
3591 "ecPointX" => super::ec::emit_ec_point_x(emit),
3592 "ecPointY" => super::ec::emit_ec_point_y(emit),
3593 _ => panic!("unknown EC builtin: {}", func_name),
3594 }
3595
3596 self.sm.push(binding_name);
3597 self.track_depth();
3598 }
3599
3600 fn lower_safediv(
3603 &mut self,
3604 binding_name: &str,
3605 args: &[String],
3606 binding_index: usize,
3607 last_uses: &HashMap<String, usize>,
3608 ) {
3609 assert!(args.len() >= 2, "safediv requires 2 arguments");
3610
3611 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3612 self.bring_to_top(&args[0], a_is_last);
3613
3614 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3615 self.bring_to_top(&args[1], b_is_last);
3616
3617 self.sm.pop();
3618 self.sm.pop();
3619
3620 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3621 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string()));
3622 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
3623 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3624
3625 self.sm.push(binding_name);
3626 self.track_depth();
3627 }
3628
3629 fn lower_safemod(
3632 &mut self,
3633 binding_name: &str,
3634 args: &[String],
3635 binding_index: usize,
3636 last_uses: &HashMap<String, usize>,
3637 ) {
3638 assert!(args.len() >= 2, "safemod requires 2 arguments");
3639
3640 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3641 self.bring_to_top(&args[0], a_is_last);
3642
3643 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3644 self.bring_to_top(&args[1], b_is_last);
3645
3646 self.sm.pop();
3647 self.sm.pop();
3648
3649 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3650 self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string()));
3651 self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
3652 self.emit_op(StackOp::Opcode("OP_MOD".to_string()));
3653
3654 self.sm.push(binding_name);
3655 self.track_depth();
3656 }
3657
3658 fn lower_clamp(
3661 &mut self,
3662 binding_name: &str,
3663 args: &[String],
3664 binding_index: usize,
3665 last_uses: &HashMap<String, usize>,
3666 ) {
3667 assert!(args.len() >= 3, "clamp requires 3 arguments");
3668
3669 let val_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3670 self.bring_to_top(&args[0], val_is_last);
3671
3672 let lo_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3673 self.bring_to_top(&args[1], lo_is_last);
3674
3675 self.sm.pop();
3676 self.sm.pop();
3677 self.emit_op(StackOp::Opcode("OP_MAX".to_string()));
3678 self.sm.push(""); let hi_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3681 self.bring_to_top(&args[2], hi_is_last);
3682
3683 self.sm.pop();
3684 self.sm.pop();
3685 self.emit_op(StackOp::Opcode("OP_MIN".to_string()));
3686
3687 self.sm.push(binding_name);
3688 self.track_depth();
3689 }
3690
3691 fn lower_pow(
3696 &mut self,
3697 binding_name: &str,
3698 args: &[String],
3699 binding_index: usize,
3700 last_uses: &HashMap<String, usize>,
3701 ) {
3702 assert!(args.len() >= 2, "pow requires 2 arguments");
3703
3704 let base_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3705 self.bring_to_top(&args[0], base_is_last);
3706
3707 let exp_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3708 self.bring_to_top(&args[1], exp_is_last);
3709
3710 self.sm.pop();
3711 self.sm.pop();
3712
3713 self.emit_op(StackOp::Swap); self.emit_op(StackOp::Push(PushValue::Int(1))); for i in 0..32 {
3718 self.emit_op(StackOp::Push(PushValue::Int(2)));
3720 self.emit_op(StackOp::Opcode("OP_PICK".to_string())); self.emit_op(StackOp::Push(PushValue::Int(i)));
3722 self.emit_op(StackOp::Opcode("OP_GREATERTHAN".to_string())); self.emit_op(StackOp::If {
3724 then_ops: vec![
3725 StackOp::Over, StackOp::Opcode("OP_MUL".to_string()), ],
3728 else_ops: vec![],
3729 });
3730 }
3731 self.emit_op(StackOp::Nip); self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3736 self.track_depth();
3737 }
3738
3739 fn lower_mul_div(
3742 &mut self,
3743 binding_name: &str,
3744 args: &[String],
3745 binding_index: usize,
3746 last_uses: &HashMap<String, usize>,
3747 ) {
3748 assert!(args.len() >= 3, "mulDiv requires 3 arguments");
3749
3750 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3751 self.bring_to_top(&args[0], a_is_last);
3752
3753 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3754 self.bring_to_top(&args[1], b_is_last);
3755
3756 self.sm.pop();
3757 self.sm.pop();
3758 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3759 self.sm.push(""); let c_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3762 self.bring_to_top(&args[2], c_is_last);
3763
3764 self.sm.pop();
3765 self.sm.pop();
3766 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3767
3768 self.sm.push(binding_name);
3769 self.track_depth();
3770 }
3771
3772 fn lower_percent_of(
3775 &mut self,
3776 binding_name: &str,
3777 args: &[String],
3778 binding_index: usize,
3779 last_uses: &HashMap<String, usize>,
3780 ) {
3781 assert!(args.len() >= 2, "percentOf requires 2 arguments");
3782
3783 let amount_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3784 self.bring_to_top(&args[0], amount_is_last);
3785
3786 let bps_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3787 self.bring_to_top(&args[1], bps_is_last);
3788
3789 self.sm.pop();
3790 self.sm.pop();
3791
3792 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3793 self.emit_op(StackOp::Push(PushValue::Int(10000)));
3794 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3795
3796 self.sm.push(binding_name);
3797 self.track_depth();
3798 }
3799
3800 fn lower_sqrt(
3804 &mut self,
3805 binding_name: &str,
3806 args: &[String],
3807 binding_index: usize,
3808 last_uses: &HashMap<String, usize>,
3809 ) {
3810 assert!(!args.is_empty(), "sqrt requires 1 argument");
3811
3812 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3813 self.bring_to_top(&args[0], n_is_last);
3814
3815 self.sm.pop();
3816
3817 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3820 let mut newton_ops = Vec::new();
3824 newton_ops.push(StackOp::Opcode("OP_DUP".to_string()));
3827 for _ in 0..16 {
3831 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())); }
3839
3840 newton_ops.push(StackOp::Opcode("OP_NIP".to_string()));
3843
3844 self.emit_op(StackOp::If {
3845 then_ops: newton_ops,
3846 else_ops: vec![], });
3848
3849 self.sm.push(binding_name);
3850 self.track_depth();
3851 }
3852
3853 fn lower_gcd(
3856 &mut self,
3857 binding_name: &str,
3858 args: &[String],
3859 binding_index: usize,
3860 last_uses: &HashMap<String, usize>,
3861 ) {
3862 assert!(args.len() >= 2, "gcd requires 2 arguments");
3863
3864 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3865 self.bring_to_top(&args[0], a_is_last);
3866
3867 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3868 self.bring_to_top(&args[1], b_is_last);
3869
3870 self.sm.pop();
3871 self.sm.pop();
3872
3873 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3876 self.emit_op(StackOp::Swap);
3877 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3878 self.emit_op(StackOp::Swap);
3879 for _ in 0..256 {
3883 self.emit_op(StackOp::Opcode("OP_DUP".to_string())); self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string())); self.emit_op(StackOp::If {
3889 then_ops: vec![
3890 StackOp::Opcode("OP_TUCK".to_string()), StackOp::Opcode("OP_MOD".to_string()), ],
3895 else_ops: vec![
3896 ],
3898 });
3899 }
3900
3901 self.emit_op(StackOp::Drop);
3904
3905 self.sm.push(binding_name);
3906 self.track_depth();
3907 }
3908
3909 fn lower_divmod(
3912 &mut self,
3913 binding_name: &str,
3914 args: &[String],
3915 binding_index: usize,
3916 last_uses: &HashMap<String, usize>,
3917 ) {
3918 assert!(args.len() >= 2, "divmod requires 2 arguments");
3919
3920 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3921 self.bring_to_top(&args[0], a_is_last);
3922
3923 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3924 self.bring_to_top(&args[1], b_is_last);
3925
3926 self.sm.pop();
3927 self.sm.pop();
3928
3929 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);
3939
3940 self.sm.push(binding_name);
3941 self.track_depth();
3942 }
3943
3944 fn lower_log2(
3954 &mut self,
3955 binding_name: &str,
3956 args: &[String],
3957 binding_index: usize,
3958 last_uses: &HashMap<String, usize>,
3959 ) {
3960 assert!(!args.is_empty(), "log2 requires 1 argument");
3961
3962 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3963 self.bring_to_top(&args[0], n_is_last);
3964
3965 self.sm.pop();
3966
3967 self.emit_op(StackOp::Push(PushValue::Int(0))); const LOG2_ITERATIONS: usize = 64;
3973 for _ in 0..LOG2_ITERATIONS {
3974 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 {
3980 then_ops: vec![
3981 StackOp::Push(PushValue::Int(2)), StackOp::Opcode("OP_DIV".to_string()), StackOp::Swap, StackOp::Opcode("OP_1ADD".to_string()), StackOp::Swap, ],
3987 else_ops: vec![],
3988 });
3989 self.emit_op(StackOp::Swap); }
3993 self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3998 self.track_depth();
3999 }
4000}
4001
4002pub fn lower_to_stack(program: &ANFProgram) -> Result<Vec<StackMethod>, String> {
4010 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4014 lower_to_stack_inner(program)
4015 }))
4016 .unwrap_or_else(|e| {
4017 if let Some(s) = e.downcast_ref::<String>() {
4018 Err(format!("stack lowering: {}", s))
4019 } else if let Some(s) = e.downcast_ref::<&str>() {
4020 Err(format!("stack lowering: {}", s))
4021 } else {
4022 Err("stack lowering: internal error".to_string())
4023 }
4024 })
4025}
4026
4027fn lower_to_stack_inner(program: &ANFProgram) -> Result<Vec<StackMethod>, String> {
4028 let mut private_methods: HashMap<String, ANFMethod> = HashMap::new();
4030 for method in &program.methods {
4031 if !method.is_public && method.name != "constructor" {
4032 private_methods.insert(method.name.clone(), method.clone());
4033 }
4034 }
4035
4036 let mut methods = Vec::new();
4037
4038 for method in &program.methods {
4039 if method.name == "constructor" || (!method.is_public && method.name != "constructor") {
4041 continue;
4042 }
4043 let sm = lower_method_with_private_methods(method, &program.properties, &private_methods)?;
4044 methods.push(sm);
4045 }
4046
4047 Ok(methods)
4048}
4049
4050fn method_uses_check_preimage(bindings: &[ANFBinding]) -> bool {
4054 bindings.iter().any(|b| matches!(&b.value, ANFValue::CheckPreimage { .. }))
4055}
4056
4057fn method_uses_code_part(bindings: &[ANFBinding]) -> bool {
4061 bindings.iter().any(|b| match &b.value {
4062 ANFValue::AddOutput { .. } | ANFValue::AddRawOutput { .. } => true,
4063 ANFValue::Call { func, .. } if func == "computeStateOutput" || func == "computeStateOutputHash" => true,
4064 ANFValue::If { then, else_branch, .. } => method_uses_code_part(then) || method_uses_code_part(else_branch),
4065 ANFValue::Loop { body, .. } => method_uses_code_part(body),
4066 _ => false,
4067 })
4068}
4069
4070fn lower_method_with_private_methods(
4071 method: &ANFMethod,
4072 properties: &[ANFProperty],
4073 private_methods: &HashMap<String, ANFMethod>,
4074) -> Result<StackMethod, String> {
4075 let mut param_names: Vec<String> = method.params.iter().map(|p| p.name.clone()).collect();
4076
4077 if method_uses_check_preimage(&method.body) {
4083 param_names.insert(0, "_opPushTxSig".to_string());
4084 if method_uses_code_part(&method.body) {
4088 param_names.insert(0, "_codePart".to_string());
4089 }
4090 }
4091
4092 let mut ctx = LoweringContext::new(¶m_names, properties);
4093 ctx.private_methods = private_methods.clone();
4094 ctx.lower_bindings(&method.body, method.is_public);
4097
4098 let has_deserialize_state = method.body.iter().any(|b| matches!(&b.value, ANFValue::DeserializeState { .. }));
4100 if method.is_public && has_deserialize_state && ctx.sm.depth() > 1 {
4101 let excess = ctx.sm.depth() - 1;
4102 for _ in 0..excess {
4103 ctx.emit_op(StackOp::Nip);
4104 ctx.sm.remove_at_depth(1);
4105 }
4106 }
4107
4108 if ctx.max_depth > MAX_STACK_DEPTH {
4109 return Err(format!(
4110 "method '{}' exceeds maximum stack depth of {} (actual: {}). Simplify the contract logic.",
4111 method.name, MAX_STACK_DEPTH, ctx.max_depth
4112 ));
4113 }
4114
4115 Ok(StackMethod {
4116 name: method.name.clone(),
4117 source_locs: ctx.source_locs,
4118 ops: ctx.ops,
4119 max_stack_depth: ctx.max_depth,
4120 })
4121}
4122
4123fn hex_to_bytes(hex_str: &str) -> Vec<u8> {
4128 if hex_str.is_empty() {
4129 return Vec::new();
4130 }
4131 assert!(
4132 hex_str.len() % 2 == 0,
4133 "invalid hex string length: {}",
4134 hex_str.len()
4135 );
4136 (0..hex_str.len())
4137 .step_by(2)
4138 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap_or(0))
4139 .collect()
4140}
4141
4142#[cfg(test)]
4147mod tests {
4148 use super::*;
4149 use crate::ir::{ANFBinding, ANFMethod, ANFParam, ANFProgram, ANFProperty, ANFValue};
4150
4151 fn p2pkh_program() -> ANFProgram {
4153 ANFProgram {
4154 contract_name: "P2PKH".to_string(),
4155 properties: vec![ANFProperty {
4156 name: "pubKeyHash".to_string(),
4157 prop_type: "Addr".to_string(),
4158 readonly: true,
4159 initial_value: None,
4160 }],
4161 methods: vec![ANFMethod {
4162 name: "unlock".to_string(),
4163 params: vec![
4164 ANFParam {
4165 name: "sig".to_string(),
4166 param_type: "Sig".to_string(),
4167 },
4168 ANFParam {
4169 name: "pubKey".to_string(),
4170 param_type: "PubKey".to_string(),
4171 },
4172 ],
4173 body: vec![
4174 ANFBinding {
4175 name: "t0".to_string(),
4176 value: ANFValue::LoadParam {
4177 name: "sig".to_string(),
4178 },
4179 source_loc: None,
4180 },
4181 ANFBinding {
4182 name: "t1".to_string(),
4183 value: ANFValue::LoadParam {
4184 name: "pubKey".to_string(),
4185 },
4186 source_loc: None,
4187 },
4188 ANFBinding {
4189 name: "t2".to_string(),
4190 value: ANFValue::LoadProp {
4191 name: "pubKeyHash".to_string(),
4192 },
4193 source_loc: None,
4194 },
4195 ANFBinding {
4196 name: "t3".to_string(),
4197 value: ANFValue::Call {
4198 func: "hash160".to_string(),
4199 args: vec!["t1".to_string()],
4200 },
4201 source_loc: None,
4202 },
4203 ANFBinding {
4204 name: "t4".to_string(),
4205 value: ANFValue::BinOp {
4206 op: "===".to_string(),
4207 left: "t3".to_string(),
4208 right: "t2".to_string(),
4209 result_type: None,
4210 },
4211 source_loc: None,
4212 },
4213 ANFBinding {
4214 name: "t5".to_string(),
4215 value: ANFValue::Assert {
4216 value: "t4".to_string(),
4217 },
4218 source_loc: None,
4219 },
4220 ANFBinding {
4221 name: "t6".to_string(),
4222 value: ANFValue::Call {
4223 func: "checkSig".to_string(),
4224 args: vec!["t0".to_string(), "t1".to_string()],
4225 },
4226 source_loc: None,
4227 },
4228 ANFBinding {
4229 name: "t7".to_string(),
4230 value: ANFValue::Assert {
4231 value: "t6".to_string(),
4232 },
4233 source_loc: None,
4234 },
4235 ],
4236 is_public: true,
4237 }],
4238 }
4239 }
4240
4241 #[test]
4242 fn test_p2pkh_stack_lowering_produces_placeholder_ops() {
4243 let program = p2pkh_program();
4244 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4245 assert_eq!(methods.len(), 1);
4246 assert_eq!(methods[0].name, "unlock");
4247
4248 let has_placeholder = methods[0].ops.iter().any(|op| {
4250 matches!(op, StackOp::Placeholder { .. })
4251 });
4252 assert!(
4253 has_placeholder,
4254 "P2PKH should have Placeholder ops for constructor params, ops: {:?}",
4255 methods[0].ops
4256 );
4257 }
4258
4259 #[test]
4260 fn test_placeholder_has_correct_param_index() {
4261 let program = p2pkh_program();
4262 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4263
4264 let placeholders: Vec<&StackOp> = methods[0]
4266 .ops
4267 .iter()
4268 .filter(|op| matches!(op, StackOp::Placeholder { .. }))
4269 .collect();
4270
4271 assert!(
4272 !placeholders.is_empty(),
4273 "should have at least one Placeholder"
4274 );
4275
4276 if let StackOp::Placeholder {
4278 param_index,
4279 param_name,
4280 } = placeholders[0]
4281 {
4282 assert_eq!(*param_index, 0);
4283 assert_eq!(param_name, "pubKeyHash");
4284 } else {
4285 panic!("expected Placeholder op");
4286 }
4287 }
4288
4289 #[test]
4290 fn test_with_initial_values_no_placeholder_ops() {
4291 let mut program = p2pkh_program();
4292 program.properties[0].initial_value =
4294 Some(serde_json::Value::String("aabbccdd".to_string()));
4295
4296 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4297 let has_placeholder = methods[0].ops.iter().any(|op| {
4298 matches!(op, StackOp::Placeholder { .. })
4299 });
4300 assert!(
4301 !has_placeholder,
4302 "with initial values, there should be no Placeholder ops"
4303 );
4304 }
4305
4306 #[test]
4307 fn test_stack_lowering_produces_standard_opcodes() {
4308 let program = p2pkh_program();
4309 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4310
4311 let opcodes: Vec<&str> = methods[0]
4313 .ops
4314 .iter()
4315 .filter_map(|op| match op {
4316 StackOp::Opcode(code) => Some(code.as_str()),
4317 _ => None,
4318 })
4319 .collect();
4320
4321 assert!(
4323 opcodes.contains(&"OP_HASH160"),
4324 "expected OP_HASH160 in opcodes: {:?}",
4325 opcodes
4326 );
4327 assert!(
4328 opcodes.contains(&"OP_CHECKSIG"),
4329 "expected OP_CHECKSIG in opcodes: {:?}",
4330 opcodes
4331 );
4332 }
4333
4334 #[test]
4335 fn test_max_stack_depth_is_tracked() {
4336 let program = p2pkh_program();
4337 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4338 assert!(
4339 methods[0].max_stack_depth > 0,
4340 "max_stack_depth should be > 0"
4341 );
4342 assert!(
4344 methods[0].max_stack_depth <= 10,
4345 "max_stack_depth should be reasonable for P2PKH, got: {}",
4346 methods[0].max_stack_depth
4347 );
4348 }
4349
4350 fn collect_all_opcodes(ops: &[StackOp]) -> Vec<String> {
4355 let mut result = Vec::new();
4356 for op in ops {
4357 match op {
4358 StackOp::Opcode(code) => result.push(code.clone()),
4359 StackOp::If { then_ops, else_ops } => {
4360 result.push("OP_IF".to_string());
4361 result.extend(collect_all_opcodes(then_ops));
4362 result.push("OP_ELSE".to_string());
4363 result.extend(collect_all_opcodes(else_ops));
4364 result.push("OP_ENDIF".to_string());
4365 }
4366 StackOp::Push(PushValue::Int(n)) => {
4367 result.push(format!("PUSH({})", n));
4368 }
4369 StackOp::Drop => result.push("OP_DROP".to_string()),
4370 StackOp::Swap => result.push("OP_SWAP".to_string()),
4371 StackOp::Dup => result.push("OP_DUP".to_string()),
4372 StackOp::Over => result.push("OP_OVER".to_string()),
4373 StackOp::Rot => result.push("OP_ROT".to_string()),
4374 StackOp::Nip => result.push("OP_NIP".to_string()),
4375 _ => {}
4376 }
4377 }
4378 result
4379 }
4380
4381 fn collect_opcodes_in_if_branches(ops: &[StackOp]) -> (Vec<String>, Vec<String>) {
4382 for op in ops {
4383 if let StackOp::If { then_ops, else_ops } = op {
4384 return (collect_all_opcodes(then_ops), collect_all_opcodes(else_ops));
4385 }
4386 }
4387 (vec![], vec![])
4388 }
4389
4390 #[test]
4395 fn test_extract_output_hash_uses_offset_40() {
4396 let program = ANFProgram {
4398 contract_name: "TestExtract".to_string(),
4399 properties: vec![ANFProperty {
4400 name: "val".to_string(),
4401 prop_type: "bigint".to_string(),
4402 readonly: false,
4403 initial_value: Some(serde_json::Value::Number(serde_json::Number::from(0))),
4404 }],
4405 methods: vec![ANFMethod {
4406 name: "check".to_string(),
4407 params: vec![
4408 ANFParam { name: "preimage".to_string(), param_type: "SigHashPreimage".to_string() },
4409 ],
4410 body: vec![
4411 ANFBinding {
4412 name: "t0".to_string(),
4413 value: ANFValue::LoadParam { name: "preimage".to_string() },
4414 source_loc: None,
4415 },
4416 ANFBinding {
4417 name: "t1".to_string(),
4418 value: ANFValue::Call {
4419 func: "extractOutputHash".to_string(),
4420 args: vec!["t0".to_string()],
4421 },
4422 source_loc: None,
4423 },
4424 ANFBinding {
4425 name: "t2".to_string(),
4426 value: ANFValue::LoadConst { value: serde_json::Value::Bool(true) },
4427 source_loc: None,
4428 },
4429 ANFBinding {
4430 name: "t3".to_string(),
4431 value: ANFValue::Assert { value: "t2".to_string() },
4432 source_loc: None,
4433 },
4434 ],
4435 is_public: true,
4436 }],
4437 };
4438
4439 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4440 let opcodes = collect_all_opcodes(&methods[0].ops);
4441
4442 assert!(
4444 opcodes.contains(&"PUSH(40)".to_string()),
4445 "extractOutputHash should use offset 40 (BIP-143 hashOutputs starts at size-40), ops: {:?}",
4446 opcodes
4447 );
4448 assert!(
4449 !opcodes.contains(&"PUSH(44)".to_string()),
4450 "extractOutputHash should NOT use offset 44, ops: {:?}",
4451 opcodes
4452 );
4453 }
4454
4455 #[test]
4460 fn test_terminal_if_propagates_terminal_assert() {
4461 let program = ANFProgram {
4464 contract_name: "TerminalIf".to_string(),
4465 properties: vec![],
4466 methods: vec![ANFMethod {
4467 name: "check".to_string(),
4468 params: vec![
4469 ANFParam { name: "mode".to_string(), param_type: "boolean".to_string() },
4470 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4471 ],
4472 body: vec![
4473 ANFBinding {
4474 name: "t0".to_string(),
4475 value: ANFValue::LoadParam { name: "mode".to_string() },
4476 source_loc: None,
4477 },
4478 ANFBinding {
4479 name: "t1".to_string(),
4480 value: ANFValue::LoadParam { name: "x".to_string() },
4481 source_loc: None,
4482 },
4483 ANFBinding {
4484 name: "t2".to_string(),
4485 value: ANFValue::If {
4486 cond: "t0".to_string(),
4487 then: vec![
4488 ANFBinding {
4489 name: "t3".to_string(),
4490 value: ANFValue::LoadConst {
4491 value: serde_json::Value::Number(serde_json::Number::from(10)),
4492 },
4493 source_loc: None,
4494 },
4495 ANFBinding {
4496 name: "t4".to_string(),
4497 value: ANFValue::BinOp {
4498 op: ">".to_string(),
4499 left: "t1".to_string(),
4500 right: "t3".to_string(),
4501 result_type: None,
4502 },
4503 source_loc: None,
4504 },
4505 ANFBinding {
4506 name: "t5".to_string(),
4507 value: ANFValue::Assert { value: "t4".to_string() },
4508 source_loc: None,
4509 },
4510 ],
4511 else_branch: vec![
4512 ANFBinding {
4513 name: "t6".to_string(),
4514 value: ANFValue::LoadConst {
4515 value: serde_json::Value::Number(serde_json::Number::from(5)),
4516 },
4517 source_loc: None,
4518 },
4519 ANFBinding {
4520 name: "t7".to_string(),
4521 value: ANFValue::BinOp {
4522 op: ">".to_string(),
4523 left: "t1".to_string(),
4524 right: "t6".to_string(),
4525 result_type: None,
4526 },
4527 source_loc: None,
4528 },
4529 ANFBinding {
4530 name: "t8".to_string(),
4531 value: ANFValue::Assert { value: "t7".to_string() },
4532 source_loc: None,
4533 },
4534 ],
4535 },
4536 source_loc: None,
4537 },
4538 ],
4539 is_public: true,
4540 }],
4541 };
4542
4543 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4544
4545 let (then_opcodes, else_opcodes) = collect_opcodes_in_if_branches(&methods[0].ops);
4547
4548 assert!(
4550 !then_opcodes.contains(&"OP_VERIFY".to_string()),
4551 "then branch should not contain OP_VERIFY (terminal assert), got: {:?}",
4552 then_opcodes
4553 );
4554 assert!(
4555 !else_opcodes.contains(&"OP_VERIFY".to_string()),
4556 "else branch should not contain OP_VERIFY (terminal assert), got: {:?}",
4557 else_opcodes
4558 );
4559 }
4560
4561 #[test]
4566 fn test_unpack_emits_bin2num() {
4567 let program = ANFProgram {
4568 contract_name: "TestUnpack".to_string(),
4569 properties: vec![],
4570 methods: vec![ANFMethod {
4571 name: "check".to_string(),
4572 params: vec![
4573 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
4574 ],
4575 body: vec![
4576 ANFBinding {
4577 name: "t0".to_string(),
4578 value: ANFValue::LoadParam { name: "data".to_string() },
4579 source_loc: None,
4580 },
4581 ANFBinding {
4582 name: "t1".to_string(),
4583 value: ANFValue::Call {
4584 func: "unpack".to_string(),
4585 args: vec!["t0".to_string()],
4586 },
4587 source_loc: None,
4588 },
4589 ANFBinding {
4590 name: "t2".to_string(),
4591 value: ANFValue::LoadConst {
4592 value: serde_json::Value::Number(serde_json::Number::from(42)),
4593 },
4594 source_loc: None,
4595 },
4596 ANFBinding {
4597 name: "t3".to_string(),
4598 value: ANFValue::BinOp {
4599 op: "===".to_string(),
4600 left: "t1".to_string(),
4601 right: "t2".to_string(),
4602 result_type: None,
4603 },
4604 source_loc: None,
4605 },
4606 ANFBinding {
4607 name: "t4".to_string(),
4608 value: ANFValue::Assert { value: "t3".to_string() },
4609 source_loc: None,
4610 },
4611 ],
4612 is_public: true,
4613 }],
4614 };
4615
4616 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4617 let opcodes = collect_all_opcodes(&methods[0].ops);
4618 assert!(
4619 opcodes.contains(&"OP_BIN2NUM".to_string()),
4620 "unpack should emit OP_BIN2NUM, got: {:?}",
4621 opcodes
4622 );
4623 }
4624
4625 #[test]
4626 fn test_pack_is_noop() {
4627 let program = ANFProgram {
4628 contract_name: "TestPack".to_string(),
4629 properties: vec![],
4630 methods: vec![ANFMethod {
4631 name: "check".to_string(),
4632 params: vec![
4633 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4634 ],
4635 body: vec![
4636 ANFBinding {
4637 name: "t0".to_string(),
4638 value: ANFValue::LoadParam { name: "x".to_string() },
4639 source_loc: None,
4640 },
4641 ANFBinding {
4642 name: "t1".to_string(),
4643 value: ANFValue::Call {
4644 func: "pack".to_string(),
4645 args: vec!["t0".to_string()],
4646 },
4647 source_loc: None,
4648 },
4649 ANFBinding {
4650 name: "t2".to_string(),
4651 value: ANFValue::LoadConst {
4652 value: serde_json::Value::Bool(true),
4653 },
4654 source_loc: None,
4655 },
4656 ANFBinding {
4657 name: "t3".to_string(),
4658 value: ANFValue::Assert { value: "t2".to_string() },
4659 source_loc: None,
4660 },
4661 ],
4662 is_public: true,
4663 }],
4664 };
4665
4666 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4667 let opcodes = collect_all_opcodes(&methods[0].ops);
4668 assert!(
4670 !opcodes.contains(&"OP_BIN2NUM".to_string()),
4671 "pack should not emit OP_BIN2NUM, got: {:?}",
4672 opcodes
4673 );
4674 assert!(
4675 !opcodes.contains(&"OP_NUM2BIN".to_string()),
4676 "pack should not emit OP_NUM2BIN, got: {:?}",
4677 opcodes
4678 );
4679 }
4680
4681 #[test]
4682 fn test_to_byte_string_is_noop() {
4683 let program = ANFProgram {
4684 contract_name: "TestToByteString".to_string(),
4685 properties: vec![],
4686 methods: vec![ANFMethod {
4687 name: "check".to_string(),
4688 params: vec![
4689 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4690 ],
4691 body: vec![
4692 ANFBinding {
4693 name: "t0".to_string(),
4694 value: ANFValue::LoadParam { name: "x".to_string() },
4695 source_loc: None,
4696 },
4697 ANFBinding {
4698 name: "t1".to_string(),
4699 value: ANFValue::Call {
4700 func: "toByteString".to_string(),
4701 args: vec!["t0".to_string()],
4702 },
4703 source_loc: None,
4704 },
4705 ANFBinding {
4706 name: "t2".to_string(),
4707 value: ANFValue::LoadConst {
4708 value: serde_json::Value::Bool(true),
4709 },
4710 source_loc: None,
4711 },
4712 ANFBinding {
4713 name: "t3".to_string(),
4714 value: ANFValue::Assert { value: "t2".to_string() },
4715 source_loc: None,
4716 },
4717 ],
4718 is_public: true,
4719 }],
4720 };
4721
4722 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4723 let opcodes = collect_all_opcodes(&methods[0].ops);
4724 assert!(
4726 !opcodes.contains(&"OP_BIN2NUM".to_string()),
4727 "toByteString should not emit OP_BIN2NUM, got: {:?}",
4728 opcodes
4729 );
4730 }
4731
4732 #[test]
4737 fn test_sqrt_has_zero_guard() {
4738 let program = ANFProgram {
4739 contract_name: "TestSqrt".to_string(),
4740 properties: vec![],
4741 methods: vec![ANFMethod {
4742 name: "check".to_string(),
4743 params: vec![
4744 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4745 ],
4746 body: vec![
4747 ANFBinding {
4748 name: "t0".to_string(),
4749 value: ANFValue::LoadParam { name: "n".to_string() },
4750 source_loc: None,
4751 },
4752 ANFBinding {
4753 name: "t1".to_string(),
4754 value: ANFValue::Call {
4755 func: "sqrt".to_string(),
4756 args: vec!["t0".to_string()],
4757 },
4758 source_loc: None,
4759 },
4760 ANFBinding {
4761 name: "t2".to_string(),
4762 value: ANFValue::LoadConst {
4763 value: serde_json::Value::Number(serde_json::Number::from(0)),
4764 },
4765 source_loc: None,
4766 },
4767 ANFBinding {
4768 name: "t3".to_string(),
4769 value: ANFValue::BinOp {
4770 op: ">=".to_string(),
4771 left: "t1".to_string(),
4772 right: "t2".to_string(),
4773 result_type: None,
4774 },
4775 source_loc: None,
4776 },
4777 ANFBinding {
4778 name: "t4".to_string(),
4779 value: ANFValue::Assert { value: "t3".to_string() },
4780 source_loc: None,
4781 },
4782 ],
4783 is_public: true,
4784 }],
4785 };
4786
4787 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4788 let opcodes = collect_all_opcodes(&methods[0].ops);
4789
4790 let dup_idx = opcodes.iter().position(|o| o == "OP_DUP");
4793 let if_idx = opcodes.iter().position(|o| o == "OP_IF");
4794
4795 assert!(
4796 dup_idx.is_some() && if_idx.is_some(),
4797 "sqrt should have OP_DUP and OP_IF for zero guard, got: {:?}",
4798 opcodes
4799 );
4800 assert!(
4801 dup_idx.unwrap() < if_idx.unwrap(),
4802 "OP_DUP should come before OP_IF in sqrt zero guard, got: {:?}",
4803 opcodes
4804 );
4805 }
4806
4807 #[test]
4812 fn test_loop_cleans_up_unused_iter_var() {
4813 let program = ANFProgram {
4817 contract_name: "TestLoopCleanup".to_string(),
4818 properties: vec![],
4819 methods: vec![ANFMethod {
4820 name: "check".to_string(),
4821 params: vec![
4822 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4823 ],
4824 body: vec![
4825 ANFBinding {
4826 name: "t0".to_string(),
4827 value: ANFValue::LoadParam { name: "x".to_string() },
4828 source_loc: None,
4829 },
4830 ANFBinding {
4831 name: "t_loop".to_string(),
4832 value: ANFValue::Loop {
4833 count: 3,
4834 body: vec![
4835 ANFBinding {
4837 name: "t1".to_string(),
4838 value: ANFValue::LoadParam { name: "x".to_string() },
4839 source_loc: None,
4840 },
4841 ANFBinding {
4842 name: "t2".to_string(),
4843 value: ANFValue::Assert { value: "t1".to_string() },
4844 source_loc: None,
4845 },
4846 ],
4847 iter_var: "__i".to_string(),
4848 },
4849 source_loc: None,
4850 },
4851 ANFBinding {
4852 name: "t_final".to_string(),
4853 value: ANFValue::LoadConst {
4854 value: serde_json::Value::Bool(true),
4855 },
4856 source_loc: None,
4857 },
4858 ANFBinding {
4859 name: "t_assert".to_string(),
4860 value: ANFValue::Assert { value: "t_final".to_string() },
4861 source_loc: None,
4862 },
4863 ],
4864 is_public: true,
4865 }],
4866 };
4867
4868 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4869 let opcodes = collect_all_opcodes(&methods[0].ops);
4870
4871 let drop_count = opcodes.iter().filter(|o| o.as_str() == "OP_DROP").count();
4875 assert!(
4876 drop_count >= 3,
4877 "unused iter var should be dropped after each iteration; expected >= 3 OP_DROPs, got {}: {:?}",
4878 drop_count,
4879 opcodes
4880 );
4881 }
4882
4883 #[test]
4888 fn test_push_value_int_large_values() {
4889 let large_val: i128 = (i64::MAX as i128) + 1;
4891 let push = PushValue::Int(large_val);
4892 if let PushValue::Int(v) = push {
4893 assert_eq!(v, large_val, "PushValue::Int should store values > i64::MAX without truncation");
4894 } else {
4895 panic!("expected PushValue::Int");
4896 }
4897
4898 let neg_val: i128 = (i64::MIN as i128) - 1;
4900 let push_neg = PushValue::Int(neg_val);
4901 if let PushValue::Int(v) = push_neg {
4902 assert_eq!(v, neg_val, "PushValue::Int should store values < i64::MIN without truncation");
4903 } else {
4904 panic!("expected PushValue::Int");
4905 }
4906 }
4907
4908 #[test]
4909 fn test_push_value_int_encodes_large_number() {
4910 use crate::codegen::emit::encode_push_int;
4912
4913 let large_val: i128 = 1i128 << 100;
4914 let (hex, _asm) = encode_push_int(large_val);
4915 assert!(!hex.is_empty(), "encoding of 2^100 should produce non-empty hex");
4917
4918 assert!(
4922 hex.len() >= 26,
4923 "2^100 should need at least 13 bytes of push data, got hex length {}: {}",
4924 hex.len(),
4925 hex
4926 );
4927 }
4928
4929 #[test]
4934 fn test_log2_uses_bit_scanning_not_byte_approx() {
4935 let program = ANFProgram {
4936 contract_name: "TestLog2".to_string(),
4937 properties: vec![],
4938 methods: vec![ANFMethod {
4939 name: "check".to_string(),
4940 params: vec![
4941 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4942 ],
4943 body: vec![
4944 ANFBinding {
4945 name: "t0".to_string(),
4946 value: ANFValue::LoadParam { name: "n".to_string() },
4947 source_loc: None,
4948 },
4949 ANFBinding {
4950 name: "t1".to_string(),
4951 value: ANFValue::Call {
4952 func: "log2".to_string(),
4953 args: vec!["t0".to_string()],
4954 },
4955 source_loc: None,
4956 },
4957 ANFBinding {
4958 name: "t2".to_string(),
4959 value: ANFValue::LoadConst {
4960 value: serde_json::Value::Number(serde_json::Number::from(0)),
4961 },
4962 source_loc: None,
4963 },
4964 ANFBinding {
4965 name: "t3".to_string(),
4966 value: ANFValue::BinOp {
4967 op: ">=".to_string(),
4968 left: "t1".to_string(),
4969 right: "t2".to_string(),
4970 result_type: None,
4971 },
4972 source_loc: None,
4973 },
4974 ANFBinding {
4975 name: "t4".to_string(),
4976 value: ANFValue::Assert { value: "t3".to_string() },
4977 source_loc: None,
4978 },
4979 ],
4980 is_public: true,
4981 }],
4982 };
4983
4984 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4985 let opcodes = collect_all_opcodes(&methods[0].ops);
4986
4987 assert!(
4989 opcodes.contains(&"OP_DIV".to_string()),
4990 "log2 should use OP_DIV (bit-scanning), got: {:?}",
4991 opcodes
4992 );
4993 assert!(
4994 opcodes.contains(&"OP_GREATERTHAN".to_string()),
4995 "log2 should use OP_GREATERTHAN (bit-scanning), got: {:?}",
4996 opcodes
4997 );
4998
4999 assert!(
5001 !opcodes.contains(&"OP_SIZE".to_string()),
5002 "log2 should NOT use OP_SIZE (old byte approximation), got: {:?}",
5003 opcodes
5004 );
5005 assert!(
5006 !opcodes.contains(&"OP_MUL".to_string()),
5007 "log2 should NOT use OP_MUL (old byte approximation), got: {:?}",
5008 opcodes
5009 );
5010
5011 assert!(
5013 opcodes.contains(&"OP_1ADD".to_string()),
5014 "log2 should use OP_1ADD (counter increment), got: {:?}",
5015 opcodes
5016 );
5017 }
5018
5019 #[test]
5024 fn test_reverse_bytes_uses_split_cat_not_op_reverse() {
5025 let program = ANFProgram {
5026 contract_name: "TestReverse".to_string(),
5027 properties: vec![],
5028 methods: vec![ANFMethod {
5029 name: "check".to_string(),
5030 params: vec![
5031 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
5032 ],
5033 body: vec![
5034 ANFBinding {
5035 name: "t0".to_string(),
5036 value: ANFValue::LoadParam { name: "data".to_string() },
5037 source_loc: None,
5038 },
5039 ANFBinding {
5040 name: "t1".to_string(),
5041 value: ANFValue::Call {
5042 func: "reverseBytes".to_string(),
5043 args: vec!["t0".to_string()],
5044 },
5045 source_loc: None,
5046 },
5047 ANFBinding {
5048 name: "t2".to_string(),
5049 value: ANFValue::LoadConst {
5050 value: serde_json::Value::Bool(true),
5051 },
5052 source_loc: None,
5053 },
5054 ANFBinding {
5055 name: "t3".to_string(),
5056 value: ANFValue::Assert { value: "t2".to_string() },
5057 source_loc: None,
5058 },
5059 ],
5060 is_public: true,
5061 }],
5062 };
5063
5064 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5065 let opcodes = collect_all_opcodes(&methods[0].ops);
5066
5067 assert!(
5069 !opcodes.contains(&"OP_REVERSE".to_string()),
5070 "reverseBytes must NOT emit OP_REVERSE (does not exist), got: {:?}",
5071 opcodes
5072 );
5073
5074 assert!(
5076 opcodes.contains(&"OP_SPLIT".to_string()),
5077 "reverseBytes should emit OP_SPLIT for byte peeling, got: {:?}",
5078 opcodes
5079 );
5080 assert!(
5081 opcodes.contains(&"OP_CAT".to_string()),
5082 "reverseBytes should emit OP_CAT for reassembly, got: {:?}",
5083 opcodes
5084 );
5085
5086 assert!(
5088 opcodes.contains(&"OP_SIZE".to_string()),
5089 "reverseBytes should emit OP_SIZE for length check, got: {:?}",
5090 opcodes
5091 );
5092 }
5093
5094 #[test]
5099 fn test_method_count_matches_public_methods() {
5100 let program = p2pkh_program();
5102 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5103
5104 assert_eq!(
5106 methods.len(),
5107 1,
5108 "expected 1 stack method (unlock), got {}: {:?}",
5109 methods.len(),
5110 methods.iter().map(|m| &m.name).collect::<Vec<_>>()
5111 );
5112 assert_eq!(methods[0].name, "unlock");
5113 }
5114
5115 #[test]
5120 fn test_multi_method_dispatch() {
5121 let program = ANFProgram {
5122 contract_name: "Multi".to_string(),
5123 properties: vec![],
5124 methods: vec![
5125 ANFMethod {
5126 name: "constructor".to_string(),
5127 params: vec![],
5128 body: vec![],
5129 is_public: false,
5130 },
5131 ANFMethod {
5132 name: "method1".to_string(),
5133 params: vec![ANFParam {
5134 name: "x".to_string(),
5135 param_type: "bigint".to_string(),
5136 }],
5137 body: vec![
5138 ANFBinding {
5139 name: "t0".to_string(),
5140 value: ANFValue::LoadParam { name: "x".to_string() },
5141 source_loc: None,
5142 },
5143 ANFBinding {
5144 name: "t1".to_string(),
5145 value: ANFValue::LoadConst {
5146 value: serde_json::Value::Number(serde_json::Number::from(42)),
5147 },
5148 source_loc: None,
5149 },
5150 ANFBinding {
5151 name: "t2".to_string(),
5152 value: ANFValue::BinOp {
5153 op: "===".to_string(),
5154 left: "t0".to_string(),
5155 right: "t1".to_string(),
5156 result_type: None,
5157 },
5158 source_loc: None,
5159 },
5160 ANFBinding {
5161 name: "t3".to_string(),
5162 value: ANFValue::Assert { value: "t2".to_string() },
5163 source_loc: None,
5164 },
5165 ],
5166 is_public: true,
5167 },
5168 ANFMethod {
5169 name: "method2".to_string(),
5170 params: vec![ANFParam {
5171 name: "y".to_string(),
5172 param_type: "bigint".to_string(),
5173 }],
5174 body: vec![
5175 ANFBinding {
5176 name: "t0".to_string(),
5177 value: ANFValue::LoadParam { name: "y".to_string() },
5178 source_loc: None,
5179 },
5180 ANFBinding {
5181 name: "t1".to_string(),
5182 value: ANFValue::LoadConst {
5183 value: serde_json::Value::Number(serde_json::Number::from(100)),
5184 },
5185 source_loc: None,
5186 },
5187 ANFBinding {
5188 name: "t2".to_string(),
5189 value: ANFValue::BinOp {
5190 op: "===".to_string(),
5191 left: "t0".to_string(),
5192 right: "t1".to_string(),
5193 result_type: None,
5194 },
5195 source_loc: None,
5196 },
5197 ANFBinding {
5198 name: "t3".to_string(),
5199 value: ANFValue::Assert { value: "t2".to_string() },
5200 source_loc: None,
5201 },
5202 ],
5203 is_public: true,
5204 },
5205 ],
5206 };
5207
5208 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5209 assert_eq!(
5210 methods.len(),
5211 2,
5212 "expected 2 stack methods, got {}: {:?}",
5213 methods.len(),
5214 methods.iter().map(|m| &m.name).collect::<Vec<_>>()
5215 );
5216 }
5217
5218 #[test]
5223 fn test_extract_outputs_uses_offset_40() {
5224 let program = ANFProgram {
5225 contract_name: "OutputsCheck".to_string(),
5226 properties: vec![],
5227 methods: vec![ANFMethod {
5228 name: "check".to_string(),
5229 params: vec![ANFParam {
5230 name: "preimage".to_string(),
5231 param_type: "SigHashPreimage".to_string(),
5232 }],
5233 body: vec![
5234 ANFBinding {
5235 name: "t0".to_string(),
5236 value: ANFValue::LoadParam { name: "preimage".to_string() },
5237 source_loc: None,
5238 },
5239 ANFBinding {
5240 name: "t1".to_string(),
5241 value: ANFValue::Call {
5242 func: "extractOutputs".to_string(),
5243 args: vec!["t0".to_string()],
5244 },
5245 source_loc: None,
5246 },
5247 ANFBinding {
5248 name: "t2".to_string(),
5249 value: ANFValue::Assert { value: "t1".to_string() },
5250 source_loc: None,
5251 },
5252 ],
5253 is_public: true,
5254 }],
5255 };
5256
5257 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5258 let opcodes = collect_all_opcodes(&methods[0].ops);
5259
5260 assert!(
5263 opcodes.contains(&"PUSH(40)".to_string()),
5264 "expected PUSH(40) for extractOutputs offset, got: {:?}",
5265 opcodes
5266 );
5267 assert!(
5269 !opcodes.contains(&"PUSH(44)".to_string()),
5270 "extractOutputs should NOT use offset 44, got: {:?}",
5271 opcodes
5272 );
5273 }
5274
5275 #[test]
5281 fn test_arithmetic_ops_contains_add() {
5282 let program = ANFProgram {
5284 contract_name: "ArithCheck".to_string(),
5285 properties: vec![ANFProperty {
5286 name: "target".to_string(),
5287 prop_type: "bigint".to_string(),
5288 readonly: true,
5289 initial_value: None,
5290 }],
5291 methods: vec![ANFMethod {
5292 name: "verify".to_string(),
5293 params: vec![
5294 ANFParam { name: "a".to_string(), param_type: "bigint".to_string() },
5295 ANFParam { name: "b".to_string(), param_type: "bigint".to_string() },
5296 ],
5297 body: vec![
5298 ANFBinding {
5299 name: "t0".to_string(),
5300 value: ANFValue::LoadParam { name: "a".to_string() },
5301 source_loc: None,
5302 },
5303 ANFBinding {
5304 name: "t1".to_string(),
5305 value: ANFValue::LoadParam { name: "b".to_string() },
5306 source_loc: None,
5307 },
5308 ANFBinding {
5309 name: "t2".to_string(),
5310 value: ANFValue::BinOp {
5311 op: "+".to_string(),
5312 left: "t0".to_string(),
5313 right: "t1".to_string(),
5314 result_type: None,
5315 },
5316 source_loc: None,
5317 },
5318 ANFBinding {
5319 name: "t3".to_string(),
5320 value: ANFValue::LoadProp { name: "target".to_string() },
5321 source_loc: None,
5322 },
5323 ANFBinding {
5324 name: "t4".to_string(),
5325 value: ANFValue::BinOp {
5326 op: "===".to_string(),
5327 left: "t2".to_string(),
5328 right: "t3".to_string(),
5329 result_type: None,
5330 },
5331 source_loc: None,
5332 },
5333 ANFBinding {
5334 name: "t5".to_string(),
5335 value: ANFValue::Assert { value: "t4".to_string() },
5336 source_loc: None,
5337 },
5338 ],
5339 is_public: true,
5340 }],
5341 };
5342
5343 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5344 let opcodes = collect_all_opcodes(&methods[0].ops);
5345
5346 assert!(
5348 opcodes.contains(&"OP_ADD".to_string()),
5349 "expected OP_ADD in stack ops for 'a + b', got: {:?}",
5350 opcodes
5351 );
5352
5353 assert!(
5355 opcodes.contains(&"OP_NUMEQUAL".to_string()),
5356 "expected OP_NUMEQUAL in stack ops for '===', got: {:?}",
5357 opcodes
5358 );
5359 }
5360
5361 #[test]
5367 fn test_s18_pick_roll_depth_within_max_stack_depth() {
5368 let program = p2pkh_program();
5369 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5370
5371 let max_depth = methods[0].max_stack_depth;
5372
5373 fn check_ops(ops: &[StackOp], max_depth: usize) {
5374 for op in ops {
5375 match op {
5376 StackOp::Pick { depth } => {
5377 assert!(
5378 *depth < max_depth,
5379 "Pick depth {} must be < max_stack_depth {}",
5380 depth,
5381 max_depth
5382 );
5383 }
5384 StackOp::Roll { depth } => {
5385 assert!(
5386 *depth < max_depth,
5387 "Roll depth {} must be < max_stack_depth {}",
5388 depth,
5389 max_depth
5390 );
5391 }
5392 StackOp::If { then_ops, else_ops } => {
5393 check_ops(then_ops, max_depth);
5394 check_ops(else_ops, max_depth);
5395 }
5396 _ => {}
5397 }
5398 }
5399 }
5400
5401 check_ops(&methods[0].ops, max_depth);
5402 }
5403
5404 #[test]
5410 fn test_bytestring_concat_emits_op_cat() {
5411 let program = ANFProgram {
5412 contract_name: "CatCheck".to_string(),
5413 properties: vec![],
5414 methods: vec![ANFMethod {
5415 name: "verify".to_string(),
5416 params: vec![
5417 ANFParam { name: "a".to_string(), param_type: "ByteString".to_string() },
5418 ANFParam { name: "b".to_string(), param_type: "ByteString".to_string() },
5419 ANFParam { name: "expected".to_string(), param_type: "ByteString".to_string() },
5420 ],
5421 body: vec![
5422 ANFBinding {
5423 name: "t0".to_string(),
5424 value: ANFValue::LoadParam { name: "a".to_string() },
5425 source_loc: None,
5426 },
5427 ANFBinding {
5428 name: "t1".to_string(),
5429 value: ANFValue::LoadParam { name: "b".to_string() },
5430 source_loc: None,
5431 },
5432 ANFBinding {
5433 name: "t2".to_string(),
5434 value: ANFValue::BinOp {
5435 op: "+".to_string(),
5436 left: "t0".to_string(),
5437 right: "t1".to_string(),
5438 result_type: Some("bytes".to_string()), },
5440 source_loc: None,
5441 },
5442 ANFBinding {
5443 name: "t3".to_string(),
5444 value: ANFValue::LoadParam { name: "expected".to_string() },
5445 source_loc: None,
5446 },
5447 ANFBinding {
5448 name: "t4".to_string(),
5449 value: ANFValue::BinOp {
5450 op: "===".to_string(),
5451 left: "t2".to_string(),
5452 right: "t3".to_string(),
5453 result_type: Some("bytes".to_string()),
5454 },
5455 source_loc: None,
5456 },
5457 ANFBinding {
5458 name: "t5".to_string(),
5459 value: ANFValue::Assert { value: "t4".to_string() },
5460 source_loc: None,
5461 },
5462 ],
5463 is_public: true,
5464 }],
5465 };
5466
5467 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5468 let opcodes = collect_all_opcodes(&methods[0].ops);
5469
5470 assert!(
5471 opcodes.contains(&"OP_CAT".to_string()),
5472 "ByteString '+' (result_type='bytes') should emit OP_CAT; got opcodes: {:?}",
5473 opcodes
5474 );
5475 assert!(
5476 !opcodes.contains(&"OP_ADD".to_string()),
5477 "ByteString '+' should NOT emit OP_ADD (that's for bigint); got opcodes: {:?}",
5478 opcodes
5479 );
5480 }
5481
5482 #[test]
5488 fn test_log2_emits_64_if_ops() {
5489 let program = ANFProgram {
5490 contract_name: "TestLog2Count".to_string(),
5491 properties: vec![],
5492 methods: vec![ANFMethod {
5493 name: "check".to_string(),
5494 params: vec![
5495 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
5496 ],
5497 body: vec![
5498 ANFBinding {
5499 name: "t0".to_string(),
5500 value: ANFValue::LoadParam { name: "n".to_string() },
5501 source_loc: None,
5502 },
5503 ANFBinding {
5504 name: "t1".to_string(),
5505 value: ANFValue::Call {
5506 func: "log2".to_string(),
5507 args: vec!["t0".to_string()],
5508 },
5509 source_loc: None,
5510 },
5511 ANFBinding {
5512 name: "t2".to_string(),
5513 value: ANFValue::LoadConst {
5514 value: serde_json::Value::Number(serde_json::Number::from(0)),
5515 },
5516 source_loc: None,
5517 },
5518 ANFBinding {
5519 name: "t3".to_string(),
5520 value: ANFValue::BinOp {
5521 op: ">=".to_string(),
5522 left: "t1".to_string(),
5523 right: "t2".to_string(),
5524 result_type: None,
5525 },
5526 source_loc: None,
5527 },
5528 ANFBinding {
5529 name: "t4".to_string(),
5530 value: ANFValue::Assert { value: "t3".to_string() },
5531 source_loc: None,
5532 },
5533 ],
5534 is_public: true,
5535 }],
5536 };
5537
5538 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5539
5540 fn count_if_ops(ops: &[StackOp]) -> usize {
5542 let mut count = 0;
5543 for op in ops {
5544 match op {
5545 StackOp::If { then_ops, else_ops } => {
5546 count += 1;
5547 count += count_if_ops(then_ops);
5548 count += count_if_ops(else_ops);
5549 }
5550 _ => {}
5551 }
5552 }
5553 count
5554 }
5555
5556 let if_count = count_if_ops(&methods[0].ops);
5557 assert_eq!(
5558 if_count, 64,
5559 "log2 should emit exactly 64 if-ops (one per bit); got {} if-ops",
5560 if_count
5561 );
5562 }
5563}