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))); const MAX_POW_ITERATIONS: i128 = 32;
3722 self.emit_op(StackOp::Push(PushValue::Int(2)));
3723 self.emit_op(StackOp::Opcode("OP_PICK".to_string())); self.emit_op(StackOp::Push(PushValue::Int(MAX_POW_ITERATIONS)));
3725 self.emit_op(StackOp::Opcode("OP_LESSTHANOREQUAL".to_string())); self.emit_op(StackOp::Opcode("OP_VERIFY".to_string()));
3727
3728 for i in 0..MAX_POW_ITERATIONS {
3729 self.emit_op(StackOp::Push(PushValue::Int(2)));
3731 self.emit_op(StackOp::Opcode("OP_PICK".to_string())); self.emit_op(StackOp::Push(PushValue::Int(i)));
3733 self.emit_op(StackOp::Opcode("OP_GREATERTHAN".to_string())); self.emit_op(StackOp::If {
3735 then_ops: vec![
3736 StackOp::Over, StackOp::Opcode("OP_MUL".to_string()), ],
3739 else_ops: vec![],
3740 });
3741 }
3742 self.emit_op(StackOp::Nip); self.emit_op(StackOp::Nip); self.sm.push(binding_name);
3747 self.track_depth();
3748 }
3749
3750 fn lower_mul_div(
3753 &mut self,
3754 binding_name: &str,
3755 args: &[String],
3756 binding_index: usize,
3757 last_uses: &HashMap<String, usize>,
3758 ) {
3759 assert!(args.len() >= 3, "mulDiv requires 3 arguments");
3760
3761 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3762 self.bring_to_top(&args[0], a_is_last);
3763
3764 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3765 self.bring_to_top(&args[1], b_is_last);
3766
3767 self.sm.pop();
3768 self.sm.pop();
3769 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3770 self.sm.push(""); let c_is_last = self.is_last_use(&args[2], binding_index, last_uses);
3773 self.bring_to_top(&args[2], c_is_last);
3774
3775 self.sm.pop();
3776 self.sm.pop();
3777 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3778
3779 self.sm.push(binding_name);
3780 self.track_depth();
3781 }
3782
3783 fn lower_percent_of(
3786 &mut self,
3787 binding_name: &str,
3788 args: &[String],
3789 binding_index: usize,
3790 last_uses: &HashMap<String, usize>,
3791 ) {
3792 assert!(args.len() >= 2, "percentOf requires 2 arguments");
3793
3794 let amount_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3795 self.bring_to_top(&args[0], amount_is_last);
3796
3797 let bps_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3798 self.bring_to_top(&args[1], bps_is_last);
3799
3800 self.sm.pop();
3801 self.sm.pop();
3802
3803 self.emit_op(StackOp::Opcode("OP_MUL".to_string()));
3804 self.emit_op(StackOp::Push(PushValue::Int(10000)));
3805 self.emit_op(StackOp::Opcode("OP_DIV".to_string()));
3806
3807 self.sm.push(binding_name);
3808 self.track_depth();
3809 }
3810
3811 fn lower_sqrt(
3815 &mut self,
3816 binding_name: &str,
3817 args: &[String],
3818 binding_index: usize,
3819 last_uses: &HashMap<String, usize>,
3820 ) {
3821 assert!(!args.is_empty(), "sqrt requires 1 argument");
3822
3823 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3824 self.bring_to_top(&args[0], n_is_last);
3825
3826 self.sm.pop();
3827
3828 self.emit_op(StackOp::Opcode("OP_DUP".to_string()));
3831 let mut newton_ops = Vec::new();
3835 newton_ops.push(StackOp::Opcode("OP_DUP".to_string()));
3838 for _ in 0..16 {
3842 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())); }
3850
3851 newton_ops.push(StackOp::Opcode("OP_NIP".to_string()));
3854
3855 self.emit_op(StackOp::If {
3856 then_ops: newton_ops,
3857 else_ops: vec![], });
3859
3860 self.sm.push(binding_name);
3861 self.track_depth();
3862 }
3863
3864 fn lower_gcd(
3867 &mut self,
3868 binding_name: &str,
3869 args: &[String],
3870 binding_index: usize,
3871 last_uses: &HashMap<String, usize>,
3872 ) {
3873 assert!(args.len() >= 2, "gcd requires 2 arguments");
3874
3875 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3876 self.bring_to_top(&args[0], a_is_last);
3877
3878 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3879 self.bring_to_top(&args[1], b_is_last);
3880
3881 self.sm.pop();
3882 self.sm.pop();
3883
3884 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3887 self.emit_op(StackOp::Swap);
3888 self.emit_op(StackOp::Opcode("OP_ABS".to_string()));
3889 self.emit_op(StackOp::Swap);
3890 for _ in 0..256 {
3894 self.emit_op(StackOp::Opcode("OP_DUP".to_string())); self.emit_op(StackOp::Opcode("OP_0NOTEQUAL".to_string())); self.emit_op(StackOp::If {
3900 then_ops: vec![
3901 StackOp::Opcode("OP_TUCK".to_string()), StackOp::Opcode("OP_MOD".to_string()), ],
3906 else_ops: vec![
3907 ],
3909 });
3910 }
3911
3912 self.emit_op(StackOp::Drop);
3915
3916 self.sm.push(binding_name);
3917 self.track_depth();
3918 }
3919
3920 fn lower_divmod(
3923 &mut self,
3924 binding_name: &str,
3925 args: &[String],
3926 binding_index: usize,
3927 last_uses: &HashMap<String, usize>,
3928 ) {
3929 assert!(args.len() >= 2, "divmod requires 2 arguments");
3930
3931 let a_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3932 self.bring_to_top(&args[0], a_is_last);
3933
3934 let b_is_last = self.is_last_use(&args[1], binding_index, last_uses);
3935 self.bring_to_top(&args[1], b_is_last);
3936
3937 self.sm.pop();
3938 self.sm.pop();
3939
3940 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);
3950
3951 self.sm.push(binding_name);
3952 self.track_depth();
3953 }
3954
3955 fn lower_log2(
3965 &mut self,
3966 binding_name: &str,
3967 args: &[String],
3968 binding_index: usize,
3969 last_uses: &HashMap<String, usize>,
3970 ) {
3971 assert!(!args.is_empty(), "log2 requires 1 argument");
3972
3973 let n_is_last = self.is_last_use(&args[0], binding_index, last_uses);
3974 self.bring_to_top(&args[0], n_is_last);
3975
3976 self.sm.pop();
3977
3978 self.emit_op(StackOp::Push(PushValue::Int(0))); const LOG2_ITERATIONS: usize = 64;
3984 for _ in 0..LOG2_ITERATIONS {
3985 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 {
3991 then_ops: vec![
3992 StackOp::Push(PushValue::Int(2)), StackOp::Opcode("OP_DIV".to_string()), StackOp::Swap, StackOp::Opcode("OP_1ADD".to_string()), StackOp::Swap, ],
3998 else_ops: vec![],
3999 });
4000 self.emit_op(StackOp::Swap); }
4004 self.emit_op(StackOp::Nip); self.sm.push(binding_name);
4009 self.track_depth();
4010 }
4011}
4012
4013pub fn lower_to_stack(program: &ANFProgram) -> Result<Vec<StackMethod>, String> {
4021 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4025 lower_to_stack_inner(program)
4026 }))
4027 .unwrap_or_else(|e| {
4028 if let Some(s) = e.downcast_ref::<String>() {
4029 Err(format!("stack lowering: {}", s))
4030 } else if let Some(s) = e.downcast_ref::<&str>() {
4031 Err(format!("stack lowering: {}", s))
4032 } else {
4033 Err("stack lowering: internal error".to_string())
4034 }
4035 })
4036}
4037
4038fn lower_to_stack_inner(program: &ANFProgram) -> Result<Vec<StackMethod>, String> {
4039 let mut private_methods: HashMap<String, ANFMethod> = HashMap::new();
4041 for method in &program.methods {
4042 if !method.is_public && method.name != "constructor" {
4043 private_methods.insert(method.name.clone(), method.clone());
4044 }
4045 }
4046
4047 let mut methods = Vec::new();
4048
4049 for method in &program.methods {
4050 if method.name == "constructor" || (!method.is_public && method.name != "constructor") {
4052 continue;
4053 }
4054 let sm = lower_method_with_private_methods(method, &program.properties, &private_methods)?;
4055 methods.push(sm);
4056 }
4057
4058 Ok(methods)
4059}
4060
4061fn method_uses_check_preimage(bindings: &[ANFBinding]) -> bool {
4065 bindings.iter().any(|b| matches!(&b.value, ANFValue::CheckPreimage { .. }))
4066}
4067
4068fn method_uses_code_part(bindings: &[ANFBinding]) -> bool {
4072 bindings.iter().any(|b| match &b.value {
4073 ANFValue::AddOutput { .. } | ANFValue::AddRawOutput { .. } => true,
4074 ANFValue::Call { func, .. } if func == "computeStateOutput" || func == "computeStateOutputHash" => true,
4075 ANFValue::If { then, else_branch, .. } => method_uses_code_part(then) || method_uses_code_part(else_branch),
4076 ANFValue::Loop { body, .. } => method_uses_code_part(body),
4077 _ => false,
4078 })
4079}
4080
4081fn lower_method_with_private_methods(
4082 method: &ANFMethod,
4083 properties: &[ANFProperty],
4084 private_methods: &HashMap<String, ANFMethod>,
4085) -> Result<StackMethod, String> {
4086 let mut param_names: Vec<String> = method.params.iter().map(|p| p.name.clone()).collect();
4087
4088 if method_uses_check_preimage(&method.body) {
4094 param_names.insert(0, "_opPushTxSig".to_string());
4095 if method_uses_code_part(&method.body) {
4099 param_names.insert(0, "_codePart".to_string());
4100 }
4101 }
4102
4103 let mut ctx = LoweringContext::new(¶m_names, properties);
4104 ctx.private_methods = private_methods.clone();
4105 ctx.lower_bindings(&method.body, method.is_public);
4108
4109 let has_deserialize_state = method.body.iter().any(|b| matches!(&b.value, ANFValue::DeserializeState { .. }));
4111 if method.is_public && has_deserialize_state && ctx.sm.depth() > 1 {
4112 let excess = ctx.sm.depth() - 1;
4113 for _ in 0..excess {
4114 ctx.emit_op(StackOp::Nip);
4115 ctx.sm.remove_at_depth(1);
4116 }
4117 }
4118
4119 if ctx.max_depth > MAX_STACK_DEPTH {
4120 return Err(format!(
4121 "method '{}' exceeds maximum stack depth of {} (actual: {}). Simplify the contract logic.",
4122 method.name, MAX_STACK_DEPTH, ctx.max_depth
4123 ));
4124 }
4125
4126 Ok(StackMethod {
4127 name: method.name.clone(),
4128 source_locs: ctx.source_locs,
4129 ops: ctx.ops,
4130 max_stack_depth: ctx.max_depth,
4131 })
4132}
4133
4134fn hex_to_bytes(hex_str: &str) -> Vec<u8> {
4139 if hex_str.is_empty() {
4140 return Vec::new();
4141 }
4142 assert!(
4143 hex_str.len() % 2 == 0,
4144 "invalid hex string length: {}",
4145 hex_str.len()
4146 );
4147 (0..hex_str.len())
4148 .step_by(2)
4149 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap_or(0))
4150 .collect()
4151}
4152
4153#[cfg(test)]
4158mod tests {
4159 use super::*;
4160 use crate::ir::{ANFBinding, ANFMethod, ANFParam, ANFProgram, ANFProperty, ANFValue};
4161
4162 fn p2pkh_program() -> ANFProgram {
4164 ANFProgram {
4165 contract_name: "P2PKH".to_string(),
4166 properties: vec![ANFProperty {
4167 name: "pubKeyHash".to_string(),
4168 prop_type: "Addr".to_string(),
4169 readonly: true,
4170 initial_value: None,
4171 }],
4172 methods: vec![ANFMethod {
4173 name: "unlock".to_string(),
4174 params: vec![
4175 ANFParam {
4176 name: "sig".to_string(),
4177 param_type: "Sig".to_string(),
4178 },
4179 ANFParam {
4180 name: "pubKey".to_string(),
4181 param_type: "PubKey".to_string(),
4182 },
4183 ],
4184 body: vec![
4185 ANFBinding {
4186 name: "t0".to_string(),
4187 value: ANFValue::LoadParam {
4188 name: "sig".to_string(),
4189 },
4190 source_loc: None,
4191 },
4192 ANFBinding {
4193 name: "t1".to_string(),
4194 value: ANFValue::LoadParam {
4195 name: "pubKey".to_string(),
4196 },
4197 source_loc: None,
4198 },
4199 ANFBinding {
4200 name: "t2".to_string(),
4201 value: ANFValue::LoadProp {
4202 name: "pubKeyHash".to_string(),
4203 },
4204 source_loc: None,
4205 },
4206 ANFBinding {
4207 name: "t3".to_string(),
4208 value: ANFValue::Call {
4209 func: "hash160".to_string(),
4210 args: vec!["t1".to_string()],
4211 },
4212 source_loc: None,
4213 },
4214 ANFBinding {
4215 name: "t4".to_string(),
4216 value: ANFValue::BinOp {
4217 op: "===".to_string(),
4218 left: "t3".to_string(),
4219 right: "t2".to_string(),
4220 result_type: None,
4221 },
4222 source_loc: None,
4223 },
4224 ANFBinding {
4225 name: "t5".to_string(),
4226 value: ANFValue::Assert {
4227 value: "t4".to_string(),
4228 },
4229 source_loc: None,
4230 },
4231 ANFBinding {
4232 name: "t6".to_string(),
4233 value: ANFValue::Call {
4234 func: "checkSig".to_string(),
4235 args: vec!["t0".to_string(), "t1".to_string()],
4236 },
4237 source_loc: None,
4238 },
4239 ANFBinding {
4240 name: "t7".to_string(),
4241 value: ANFValue::Assert {
4242 value: "t6".to_string(),
4243 },
4244 source_loc: None,
4245 },
4246 ],
4247 is_public: true,
4248 }],
4249 }
4250 }
4251
4252 #[test]
4253 fn test_p2pkh_stack_lowering_produces_placeholder_ops() {
4254 let program = p2pkh_program();
4255 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4256 assert_eq!(methods.len(), 1);
4257 assert_eq!(methods[0].name, "unlock");
4258
4259 let has_placeholder = methods[0].ops.iter().any(|op| {
4261 matches!(op, StackOp::Placeholder { .. })
4262 });
4263 assert!(
4264 has_placeholder,
4265 "P2PKH should have Placeholder ops for constructor params, ops: {:?}",
4266 methods[0].ops
4267 );
4268 }
4269
4270 #[test]
4271 fn test_placeholder_has_correct_param_index() {
4272 let program = p2pkh_program();
4273 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4274
4275 let placeholders: Vec<&StackOp> = methods[0]
4277 .ops
4278 .iter()
4279 .filter(|op| matches!(op, StackOp::Placeholder { .. }))
4280 .collect();
4281
4282 assert!(
4283 !placeholders.is_empty(),
4284 "should have at least one Placeholder"
4285 );
4286
4287 if let StackOp::Placeholder {
4289 param_index,
4290 param_name,
4291 } = placeholders[0]
4292 {
4293 assert_eq!(*param_index, 0);
4294 assert_eq!(param_name, "pubKeyHash");
4295 } else {
4296 panic!("expected Placeholder op");
4297 }
4298 }
4299
4300 #[test]
4301 fn test_with_initial_values_no_placeholder_ops() {
4302 let mut program = p2pkh_program();
4303 program.properties[0].initial_value =
4305 Some(serde_json::Value::String("aabbccdd".to_string()));
4306
4307 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4308 let has_placeholder = methods[0].ops.iter().any(|op| {
4309 matches!(op, StackOp::Placeholder { .. })
4310 });
4311 assert!(
4312 !has_placeholder,
4313 "with initial values, there should be no Placeholder ops"
4314 );
4315 }
4316
4317 #[test]
4318 fn test_stack_lowering_produces_standard_opcodes() {
4319 let program = p2pkh_program();
4320 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4321
4322 let opcodes: Vec<&str> = methods[0]
4324 .ops
4325 .iter()
4326 .filter_map(|op| match op {
4327 StackOp::Opcode(code) => Some(code.as_str()),
4328 _ => None,
4329 })
4330 .collect();
4331
4332 assert!(
4334 opcodes.contains(&"OP_HASH160"),
4335 "expected OP_HASH160 in opcodes: {:?}",
4336 opcodes
4337 );
4338 assert!(
4339 opcodes.contains(&"OP_CHECKSIG"),
4340 "expected OP_CHECKSIG in opcodes: {:?}",
4341 opcodes
4342 );
4343 }
4344
4345 #[test]
4346 fn test_max_stack_depth_is_tracked() {
4347 let program = p2pkh_program();
4348 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4349 assert!(
4350 methods[0].max_stack_depth > 0,
4351 "max_stack_depth should be > 0"
4352 );
4353 assert!(
4355 methods[0].max_stack_depth <= 10,
4356 "max_stack_depth should be reasonable for P2PKH, got: {}",
4357 methods[0].max_stack_depth
4358 );
4359 }
4360
4361 fn collect_all_opcodes(ops: &[StackOp]) -> Vec<String> {
4366 let mut result = Vec::new();
4367 for op in ops {
4368 match op {
4369 StackOp::Opcode(code) => result.push(code.clone()),
4370 StackOp::If { then_ops, else_ops } => {
4371 result.push("OP_IF".to_string());
4372 result.extend(collect_all_opcodes(then_ops));
4373 result.push("OP_ELSE".to_string());
4374 result.extend(collect_all_opcodes(else_ops));
4375 result.push("OP_ENDIF".to_string());
4376 }
4377 StackOp::Push(PushValue::Int(n)) => {
4378 result.push(format!("PUSH({})", n));
4379 }
4380 StackOp::Drop => result.push("OP_DROP".to_string()),
4381 StackOp::Swap => result.push("OP_SWAP".to_string()),
4382 StackOp::Dup => result.push("OP_DUP".to_string()),
4383 StackOp::Over => result.push("OP_OVER".to_string()),
4384 StackOp::Rot => result.push("OP_ROT".to_string()),
4385 StackOp::Nip => result.push("OP_NIP".to_string()),
4386 _ => {}
4387 }
4388 }
4389 result
4390 }
4391
4392 fn collect_opcodes_in_if_branches(ops: &[StackOp]) -> (Vec<String>, Vec<String>) {
4393 for op in ops {
4394 if let StackOp::If { then_ops, else_ops } = op {
4395 return (collect_all_opcodes(then_ops), collect_all_opcodes(else_ops));
4396 }
4397 }
4398 (vec![], vec![])
4399 }
4400
4401 #[test]
4406 fn test_extract_output_hash_uses_offset_40() {
4407 let program = ANFProgram {
4409 contract_name: "TestExtract".to_string(),
4410 properties: vec![ANFProperty {
4411 name: "val".to_string(),
4412 prop_type: "bigint".to_string(),
4413 readonly: false,
4414 initial_value: Some(serde_json::Value::Number(serde_json::Number::from(0))),
4415 }],
4416 methods: vec![ANFMethod {
4417 name: "check".to_string(),
4418 params: vec![
4419 ANFParam { name: "preimage".to_string(), param_type: "SigHashPreimage".to_string() },
4420 ],
4421 body: vec![
4422 ANFBinding {
4423 name: "t0".to_string(),
4424 value: ANFValue::LoadParam { name: "preimage".to_string() },
4425 source_loc: None,
4426 },
4427 ANFBinding {
4428 name: "t1".to_string(),
4429 value: ANFValue::Call {
4430 func: "extractOutputHash".to_string(),
4431 args: vec!["t0".to_string()],
4432 },
4433 source_loc: None,
4434 },
4435 ANFBinding {
4436 name: "t2".to_string(),
4437 value: ANFValue::LoadConst { value: serde_json::Value::Bool(true) },
4438 source_loc: None,
4439 },
4440 ANFBinding {
4441 name: "t3".to_string(),
4442 value: ANFValue::Assert { value: "t2".to_string() },
4443 source_loc: None,
4444 },
4445 ],
4446 is_public: true,
4447 }],
4448 };
4449
4450 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4451 let opcodes = collect_all_opcodes(&methods[0].ops);
4452
4453 assert!(
4455 opcodes.contains(&"PUSH(40)".to_string()),
4456 "extractOutputHash should use offset 40 (BIP-143 hashOutputs starts at size-40), ops: {:?}",
4457 opcodes
4458 );
4459 assert!(
4460 !opcodes.contains(&"PUSH(44)".to_string()),
4461 "extractOutputHash should NOT use offset 44, ops: {:?}",
4462 opcodes
4463 );
4464 }
4465
4466 #[test]
4471 fn test_terminal_if_propagates_terminal_assert() {
4472 let program = ANFProgram {
4475 contract_name: "TerminalIf".to_string(),
4476 properties: vec![],
4477 methods: vec![ANFMethod {
4478 name: "check".to_string(),
4479 params: vec![
4480 ANFParam { name: "mode".to_string(), param_type: "boolean".to_string() },
4481 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4482 ],
4483 body: vec![
4484 ANFBinding {
4485 name: "t0".to_string(),
4486 value: ANFValue::LoadParam { name: "mode".to_string() },
4487 source_loc: None,
4488 },
4489 ANFBinding {
4490 name: "t1".to_string(),
4491 value: ANFValue::LoadParam { name: "x".to_string() },
4492 source_loc: None,
4493 },
4494 ANFBinding {
4495 name: "t2".to_string(),
4496 value: ANFValue::If {
4497 cond: "t0".to_string(),
4498 then: vec![
4499 ANFBinding {
4500 name: "t3".to_string(),
4501 value: ANFValue::LoadConst {
4502 value: serde_json::Value::Number(serde_json::Number::from(10)),
4503 },
4504 source_loc: None,
4505 },
4506 ANFBinding {
4507 name: "t4".to_string(),
4508 value: ANFValue::BinOp {
4509 op: ">".to_string(),
4510 left: "t1".to_string(),
4511 right: "t3".to_string(),
4512 result_type: None,
4513 },
4514 source_loc: None,
4515 },
4516 ANFBinding {
4517 name: "t5".to_string(),
4518 value: ANFValue::Assert { value: "t4".to_string() },
4519 source_loc: None,
4520 },
4521 ],
4522 else_branch: vec![
4523 ANFBinding {
4524 name: "t6".to_string(),
4525 value: ANFValue::LoadConst {
4526 value: serde_json::Value::Number(serde_json::Number::from(5)),
4527 },
4528 source_loc: None,
4529 },
4530 ANFBinding {
4531 name: "t7".to_string(),
4532 value: ANFValue::BinOp {
4533 op: ">".to_string(),
4534 left: "t1".to_string(),
4535 right: "t6".to_string(),
4536 result_type: None,
4537 },
4538 source_loc: None,
4539 },
4540 ANFBinding {
4541 name: "t8".to_string(),
4542 value: ANFValue::Assert { value: "t7".to_string() },
4543 source_loc: None,
4544 },
4545 ],
4546 },
4547 source_loc: None,
4548 },
4549 ],
4550 is_public: true,
4551 }],
4552 };
4553
4554 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4555
4556 let (then_opcodes, else_opcodes) = collect_opcodes_in_if_branches(&methods[0].ops);
4558
4559 assert!(
4561 !then_opcodes.contains(&"OP_VERIFY".to_string()),
4562 "then branch should not contain OP_VERIFY (terminal assert), got: {:?}",
4563 then_opcodes
4564 );
4565 assert!(
4566 !else_opcodes.contains(&"OP_VERIFY".to_string()),
4567 "else branch should not contain OP_VERIFY (terminal assert), got: {:?}",
4568 else_opcodes
4569 );
4570 }
4571
4572 #[test]
4577 fn test_unpack_emits_bin2num() {
4578 let program = ANFProgram {
4579 contract_name: "TestUnpack".to_string(),
4580 properties: vec![],
4581 methods: vec![ANFMethod {
4582 name: "check".to_string(),
4583 params: vec![
4584 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
4585 ],
4586 body: vec![
4587 ANFBinding {
4588 name: "t0".to_string(),
4589 value: ANFValue::LoadParam { name: "data".to_string() },
4590 source_loc: None,
4591 },
4592 ANFBinding {
4593 name: "t1".to_string(),
4594 value: ANFValue::Call {
4595 func: "unpack".to_string(),
4596 args: vec!["t0".to_string()],
4597 },
4598 source_loc: None,
4599 },
4600 ANFBinding {
4601 name: "t2".to_string(),
4602 value: ANFValue::LoadConst {
4603 value: serde_json::Value::Number(serde_json::Number::from(42)),
4604 },
4605 source_loc: None,
4606 },
4607 ANFBinding {
4608 name: "t3".to_string(),
4609 value: ANFValue::BinOp {
4610 op: "===".to_string(),
4611 left: "t1".to_string(),
4612 right: "t2".to_string(),
4613 result_type: None,
4614 },
4615 source_loc: None,
4616 },
4617 ANFBinding {
4618 name: "t4".to_string(),
4619 value: ANFValue::Assert { value: "t3".to_string() },
4620 source_loc: None,
4621 },
4622 ],
4623 is_public: true,
4624 }],
4625 };
4626
4627 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4628 let opcodes = collect_all_opcodes(&methods[0].ops);
4629 assert!(
4630 opcodes.contains(&"OP_BIN2NUM".to_string()),
4631 "unpack should emit OP_BIN2NUM, got: {:?}",
4632 opcodes
4633 );
4634 }
4635
4636 #[test]
4637 fn test_pack_is_noop() {
4638 let program = ANFProgram {
4639 contract_name: "TestPack".to_string(),
4640 properties: vec![],
4641 methods: vec![ANFMethod {
4642 name: "check".to_string(),
4643 params: vec![
4644 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4645 ],
4646 body: vec![
4647 ANFBinding {
4648 name: "t0".to_string(),
4649 value: ANFValue::LoadParam { name: "x".to_string() },
4650 source_loc: None,
4651 },
4652 ANFBinding {
4653 name: "t1".to_string(),
4654 value: ANFValue::Call {
4655 func: "pack".to_string(),
4656 args: vec!["t0".to_string()],
4657 },
4658 source_loc: None,
4659 },
4660 ANFBinding {
4661 name: "t2".to_string(),
4662 value: ANFValue::LoadConst {
4663 value: serde_json::Value::Bool(true),
4664 },
4665 source_loc: None,
4666 },
4667 ANFBinding {
4668 name: "t3".to_string(),
4669 value: ANFValue::Assert { value: "t2".to_string() },
4670 source_loc: None,
4671 },
4672 ],
4673 is_public: true,
4674 }],
4675 };
4676
4677 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4678 let opcodes = collect_all_opcodes(&methods[0].ops);
4679 assert!(
4681 !opcodes.contains(&"OP_BIN2NUM".to_string()),
4682 "pack should not emit OP_BIN2NUM, got: {:?}",
4683 opcodes
4684 );
4685 assert!(
4686 !opcodes.contains(&"OP_NUM2BIN".to_string()),
4687 "pack should not emit OP_NUM2BIN, got: {:?}",
4688 opcodes
4689 );
4690 }
4691
4692 #[test]
4693 fn test_to_byte_string_is_noop() {
4694 let program = ANFProgram {
4695 contract_name: "TestToByteString".to_string(),
4696 properties: vec![],
4697 methods: vec![ANFMethod {
4698 name: "check".to_string(),
4699 params: vec![
4700 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4701 ],
4702 body: vec![
4703 ANFBinding {
4704 name: "t0".to_string(),
4705 value: ANFValue::LoadParam { name: "x".to_string() },
4706 source_loc: None,
4707 },
4708 ANFBinding {
4709 name: "t1".to_string(),
4710 value: ANFValue::Call {
4711 func: "toByteString".to_string(),
4712 args: vec!["t0".to_string()],
4713 },
4714 source_loc: None,
4715 },
4716 ANFBinding {
4717 name: "t2".to_string(),
4718 value: ANFValue::LoadConst {
4719 value: serde_json::Value::Bool(true),
4720 },
4721 source_loc: None,
4722 },
4723 ANFBinding {
4724 name: "t3".to_string(),
4725 value: ANFValue::Assert { value: "t2".to_string() },
4726 source_loc: None,
4727 },
4728 ],
4729 is_public: true,
4730 }],
4731 };
4732
4733 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4734 let opcodes = collect_all_opcodes(&methods[0].ops);
4735 assert!(
4737 !opcodes.contains(&"OP_BIN2NUM".to_string()),
4738 "toByteString should not emit OP_BIN2NUM, got: {:?}",
4739 opcodes
4740 );
4741 }
4742
4743 #[test]
4748 fn test_sqrt_has_zero_guard() {
4749 let program = ANFProgram {
4750 contract_name: "TestSqrt".to_string(),
4751 properties: vec![],
4752 methods: vec![ANFMethod {
4753 name: "check".to_string(),
4754 params: vec![
4755 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4756 ],
4757 body: vec![
4758 ANFBinding {
4759 name: "t0".to_string(),
4760 value: ANFValue::LoadParam { name: "n".to_string() },
4761 source_loc: None,
4762 },
4763 ANFBinding {
4764 name: "t1".to_string(),
4765 value: ANFValue::Call {
4766 func: "sqrt".to_string(),
4767 args: vec!["t0".to_string()],
4768 },
4769 source_loc: None,
4770 },
4771 ANFBinding {
4772 name: "t2".to_string(),
4773 value: ANFValue::LoadConst {
4774 value: serde_json::Value::Number(serde_json::Number::from(0)),
4775 },
4776 source_loc: None,
4777 },
4778 ANFBinding {
4779 name: "t3".to_string(),
4780 value: ANFValue::BinOp {
4781 op: ">=".to_string(),
4782 left: "t1".to_string(),
4783 right: "t2".to_string(),
4784 result_type: None,
4785 },
4786 source_loc: None,
4787 },
4788 ANFBinding {
4789 name: "t4".to_string(),
4790 value: ANFValue::Assert { value: "t3".to_string() },
4791 source_loc: None,
4792 },
4793 ],
4794 is_public: true,
4795 }],
4796 };
4797
4798 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4799 let opcodes = collect_all_opcodes(&methods[0].ops);
4800
4801 let dup_idx = opcodes.iter().position(|o| o == "OP_DUP");
4804 let if_idx = opcodes.iter().position(|o| o == "OP_IF");
4805
4806 assert!(
4807 dup_idx.is_some() && if_idx.is_some(),
4808 "sqrt should have OP_DUP and OP_IF for zero guard, got: {:?}",
4809 opcodes
4810 );
4811 assert!(
4812 dup_idx.unwrap() < if_idx.unwrap(),
4813 "OP_DUP should come before OP_IF in sqrt zero guard, got: {:?}",
4814 opcodes
4815 );
4816 }
4817
4818 #[test]
4823 fn test_loop_cleans_up_unused_iter_var() {
4824 let program = ANFProgram {
4828 contract_name: "TestLoopCleanup".to_string(),
4829 properties: vec![],
4830 methods: vec![ANFMethod {
4831 name: "check".to_string(),
4832 params: vec![
4833 ANFParam { name: "x".to_string(), param_type: "bigint".to_string() },
4834 ],
4835 body: vec![
4836 ANFBinding {
4837 name: "t0".to_string(),
4838 value: ANFValue::LoadParam { name: "x".to_string() },
4839 source_loc: None,
4840 },
4841 ANFBinding {
4842 name: "t_loop".to_string(),
4843 value: ANFValue::Loop {
4844 count: 3,
4845 body: vec![
4846 ANFBinding {
4848 name: "t1".to_string(),
4849 value: ANFValue::LoadParam { name: "x".to_string() },
4850 source_loc: None,
4851 },
4852 ANFBinding {
4853 name: "t2".to_string(),
4854 value: ANFValue::Assert { value: "t1".to_string() },
4855 source_loc: None,
4856 },
4857 ],
4858 iter_var: "__i".to_string(),
4859 },
4860 source_loc: None,
4861 },
4862 ANFBinding {
4863 name: "t_final".to_string(),
4864 value: ANFValue::LoadConst {
4865 value: serde_json::Value::Bool(true),
4866 },
4867 source_loc: None,
4868 },
4869 ANFBinding {
4870 name: "t_assert".to_string(),
4871 value: ANFValue::Assert { value: "t_final".to_string() },
4872 source_loc: None,
4873 },
4874 ],
4875 is_public: true,
4876 }],
4877 };
4878
4879 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4880 let opcodes = collect_all_opcodes(&methods[0].ops);
4881
4882 let drop_count = opcodes.iter().filter(|o| o.as_str() == "OP_DROP").count();
4886 assert!(
4887 drop_count >= 3,
4888 "unused iter var should be dropped after each iteration; expected >= 3 OP_DROPs, got {}: {:?}",
4889 drop_count,
4890 opcodes
4891 );
4892 }
4893
4894 #[test]
4899 fn test_push_value_int_large_values() {
4900 let large_val: i128 = (i64::MAX as i128) + 1;
4902 let push = PushValue::Int(large_val);
4903 if let PushValue::Int(v) = push {
4904 assert_eq!(v, large_val, "PushValue::Int should store values > i64::MAX without truncation");
4905 } else {
4906 panic!("expected PushValue::Int");
4907 }
4908
4909 let neg_val: i128 = (i64::MIN as i128) - 1;
4911 let push_neg = PushValue::Int(neg_val);
4912 if let PushValue::Int(v) = push_neg {
4913 assert_eq!(v, neg_val, "PushValue::Int should store values < i64::MIN without truncation");
4914 } else {
4915 panic!("expected PushValue::Int");
4916 }
4917 }
4918
4919 #[test]
4920 fn test_push_value_int_encodes_large_number() {
4921 use crate::codegen::emit::encode_push_int;
4923
4924 let large_val: i128 = 1i128 << 100;
4925 let (hex, _asm) = encode_push_int(large_val);
4926 assert!(!hex.is_empty(), "encoding of 2^100 should produce non-empty hex");
4928
4929 assert!(
4933 hex.len() >= 26,
4934 "2^100 should need at least 13 bytes of push data, got hex length {}: {}",
4935 hex.len(),
4936 hex
4937 );
4938 }
4939
4940 #[test]
4945 fn test_log2_uses_bit_scanning_not_byte_approx() {
4946 let program = ANFProgram {
4947 contract_name: "TestLog2".to_string(),
4948 properties: vec![],
4949 methods: vec![ANFMethod {
4950 name: "check".to_string(),
4951 params: vec![
4952 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
4953 ],
4954 body: vec![
4955 ANFBinding {
4956 name: "t0".to_string(),
4957 value: ANFValue::LoadParam { name: "n".to_string() },
4958 source_loc: None,
4959 },
4960 ANFBinding {
4961 name: "t1".to_string(),
4962 value: ANFValue::Call {
4963 func: "log2".to_string(),
4964 args: vec!["t0".to_string()],
4965 },
4966 source_loc: None,
4967 },
4968 ANFBinding {
4969 name: "t2".to_string(),
4970 value: ANFValue::LoadConst {
4971 value: serde_json::Value::Number(serde_json::Number::from(0)),
4972 },
4973 source_loc: None,
4974 },
4975 ANFBinding {
4976 name: "t3".to_string(),
4977 value: ANFValue::BinOp {
4978 op: ">=".to_string(),
4979 left: "t1".to_string(),
4980 right: "t2".to_string(),
4981 result_type: None,
4982 },
4983 source_loc: None,
4984 },
4985 ANFBinding {
4986 name: "t4".to_string(),
4987 value: ANFValue::Assert { value: "t3".to_string() },
4988 source_loc: None,
4989 },
4990 ],
4991 is_public: true,
4992 }],
4993 };
4994
4995 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
4996 let opcodes = collect_all_opcodes(&methods[0].ops);
4997
4998 assert!(
5000 opcodes.contains(&"OP_DIV".to_string()),
5001 "log2 should use OP_DIV (bit-scanning), got: {:?}",
5002 opcodes
5003 );
5004 assert!(
5005 opcodes.contains(&"OP_GREATERTHAN".to_string()),
5006 "log2 should use OP_GREATERTHAN (bit-scanning), got: {:?}",
5007 opcodes
5008 );
5009
5010 assert!(
5012 !opcodes.contains(&"OP_SIZE".to_string()),
5013 "log2 should NOT use OP_SIZE (old byte approximation), got: {:?}",
5014 opcodes
5015 );
5016 assert!(
5017 !opcodes.contains(&"OP_MUL".to_string()),
5018 "log2 should NOT use OP_MUL (old byte approximation), got: {:?}",
5019 opcodes
5020 );
5021
5022 assert!(
5024 opcodes.contains(&"OP_1ADD".to_string()),
5025 "log2 should use OP_1ADD (counter increment), got: {:?}",
5026 opcodes
5027 );
5028 }
5029
5030 #[test]
5035 fn test_reverse_bytes_uses_split_cat_not_op_reverse() {
5036 let program = ANFProgram {
5037 contract_name: "TestReverse".to_string(),
5038 properties: vec![],
5039 methods: vec![ANFMethod {
5040 name: "check".to_string(),
5041 params: vec![
5042 ANFParam { name: "data".to_string(), param_type: "ByteString".to_string() },
5043 ],
5044 body: vec![
5045 ANFBinding {
5046 name: "t0".to_string(),
5047 value: ANFValue::LoadParam { name: "data".to_string() },
5048 source_loc: None,
5049 },
5050 ANFBinding {
5051 name: "t1".to_string(),
5052 value: ANFValue::Call {
5053 func: "reverseBytes".to_string(),
5054 args: vec!["t0".to_string()],
5055 },
5056 source_loc: None,
5057 },
5058 ANFBinding {
5059 name: "t2".to_string(),
5060 value: ANFValue::LoadConst {
5061 value: serde_json::Value::Bool(true),
5062 },
5063 source_loc: None,
5064 },
5065 ANFBinding {
5066 name: "t3".to_string(),
5067 value: ANFValue::Assert { value: "t2".to_string() },
5068 source_loc: None,
5069 },
5070 ],
5071 is_public: true,
5072 }],
5073 };
5074
5075 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5076 let opcodes = collect_all_opcodes(&methods[0].ops);
5077
5078 assert!(
5080 !opcodes.contains(&"OP_REVERSE".to_string()),
5081 "reverseBytes must NOT emit OP_REVERSE (does not exist), got: {:?}",
5082 opcodes
5083 );
5084
5085 assert!(
5087 opcodes.contains(&"OP_SPLIT".to_string()),
5088 "reverseBytes should emit OP_SPLIT for byte peeling, got: {:?}",
5089 opcodes
5090 );
5091 assert!(
5092 opcodes.contains(&"OP_CAT".to_string()),
5093 "reverseBytes should emit OP_CAT for reassembly, got: {:?}",
5094 opcodes
5095 );
5096
5097 assert!(
5099 opcodes.contains(&"OP_SIZE".to_string()),
5100 "reverseBytes should emit OP_SIZE for length check, got: {:?}",
5101 opcodes
5102 );
5103 }
5104
5105 #[test]
5110 fn test_method_count_matches_public_methods() {
5111 let program = p2pkh_program();
5113 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5114
5115 assert_eq!(
5117 methods.len(),
5118 1,
5119 "expected 1 stack method (unlock), got {}: {:?}",
5120 methods.len(),
5121 methods.iter().map(|m| &m.name).collect::<Vec<_>>()
5122 );
5123 assert_eq!(methods[0].name, "unlock");
5124 }
5125
5126 #[test]
5131 fn test_multi_method_dispatch() {
5132 let program = ANFProgram {
5133 contract_name: "Multi".to_string(),
5134 properties: vec![],
5135 methods: vec![
5136 ANFMethod {
5137 name: "constructor".to_string(),
5138 params: vec![],
5139 body: vec![],
5140 is_public: false,
5141 },
5142 ANFMethod {
5143 name: "method1".to_string(),
5144 params: vec![ANFParam {
5145 name: "x".to_string(),
5146 param_type: "bigint".to_string(),
5147 }],
5148 body: vec![
5149 ANFBinding {
5150 name: "t0".to_string(),
5151 value: ANFValue::LoadParam { name: "x".to_string() },
5152 source_loc: None,
5153 },
5154 ANFBinding {
5155 name: "t1".to_string(),
5156 value: ANFValue::LoadConst {
5157 value: serde_json::Value::Number(serde_json::Number::from(42)),
5158 },
5159 source_loc: None,
5160 },
5161 ANFBinding {
5162 name: "t2".to_string(),
5163 value: ANFValue::BinOp {
5164 op: "===".to_string(),
5165 left: "t0".to_string(),
5166 right: "t1".to_string(),
5167 result_type: None,
5168 },
5169 source_loc: None,
5170 },
5171 ANFBinding {
5172 name: "t3".to_string(),
5173 value: ANFValue::Assert { value: "t2".to_string() },
5174 source_loc: None,
5175 },
5176 ],
5177 is_public: true,
5178 },
5179 ANFMethod {
5180 name: "method2".to_string(),
5181 params: vec![ANFParam {
5182 name: "y".to_string(),
5183 param_type: "bigint".to_string(),
5184 }],
5185 body: vec![
5186 ANFBinding {
5187 name: "t0".to_string(),
5188 value: ANFValue::LoadParam { name: "y".to_string() },
5189 source_loc: None,
5190 },
5191 ANFBinding {
5192 name: "t1".to_string(),
5193 value: ANFValue::LoadConst {
5194 value: serde_json::Value::Number(serde_json::Number::from(100)),
5195 },
5196 source_loc: None,
5197 },
5198 ANFBinding {
5199 name: "t2".to_string(),
5200 value: ANFValue::BinOp {
5201 op: "===".to_string(),
5202 left: "t0".to_string(),
5203 right: "t1".to_string(),
5204 result_type: None,
5205 },
5206 source_loc: None,
5207 },
5208 ANFBinding {
5209 name: "t3".to_string(),
5210 value: ANFValue::Assert { value: "t2".to_string() },
5211 source_loc: None,
5212 },
5213 ],
5214 is_public: true,
5215 },
5216 ],
5217 };
5218
5219 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5220 assert_eq!(
5221 methods.len(),
5222 2,
5223 "expected 2 stack methods, got {}: {:?}",
5224 methods.len(),
5225 methods.iter().map(|m| &m.name).collect::<Vec<_>>()
5226 );
5227 }
5228
5229 #[test]
5234 fn test_extract_outputs_uses_offset_40() {
5235 let program = ANFProgram {
5236 contract_name: "OutputsCheck".to_string(),
5237 properties: vec![],
5238 methods: vec![ANFMethod {
5239 name: "check".to_string(),
5240 params: vec![ANFParam {
5241 name: "preimage".to_string(),
5242 param_type: "SigHashPreimage".to_string(),
5243 }],
5244 body: vec![
5245 ANFBinding {
5246 name: "t0".to_string(),
5247 value: ANFValue::LoadParam { name: "preimage".to_string() },
5248 source_loc: None,
5249 },
5250 ANFBinding {
5251 name: "t1".to_string(),
5252 value: ANFValue::Call {
5253 func: "extractOutputs".to_string(),
5254 args: vec!["t0".to_string()],
5255 },
5256 source_loc: None,
5257 },
5258 ANFBinding {
5259 name: "t2".to_string(),
5260 value: ANFValue::Assert { value: "t1".to_string() },
5261 source_loc: None,
5262 },
5263 ],
5264 is_public: true,
5265 }],
5266 };
5267
5268 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5269 let opcodes = collect_all_opcodes(&methods[0].ops);
5270
5271 assert!(
5274 opcodes.contains(&"PUSH(40)".to_string()),
5275 "expected PUSH(40) for extractOutputs offset, got: {:?}",
5276 opcodes
5277 );
5278 assert!(
5280 !opcodes.contains(&"PUSH(44)".to_string()),
5281 "extractOutputs should NOT use offset 44, got: {:?}",
5282 opcodes
5283 );
5284 }
5285
5286 #[test]
5292 fn test_arithmetic_ops_contains_add() {
5293 let program = ANFProgram {
5295 contract_name: "ArithCheck".to_string(),
5296 properties: vec![ANFProperty {
5297 name: "target".to_string(),
5298 prop_type: "bigint".to_string(),
5299 readonly: true,
5300 initial_value: None,
5301 }],
5302 methods: vec![ANFMethod {
5303 name: "verify".to_string(),
5304 params: vec![
5305 ANFParam { name: "a".to_string(), param_type: "bigint".to_string() },
5306 ANFParam { name: "b".to_string(), param_type: "bigint".to_string() },
5307 ],
5308 body: vec![
5309 ANFBinding {
5310 name: "t0".to_string(),
5311 value: ANFValue::LoadParam { name: "a".to_string() },
5312 source_loc: None,
5313 },
5314 ANFBinding {
5315 name: "t1".to_string(),
5316 value: ANFValue::LoadParam { name: "b".to_string() },
5317 source_loc: None,
5318 },
5319 ANFBinding {
5320 name: "t2".to_string(),
5321 value: ANFValue::BinOp {
5322 op: "+".to_string(),
5323 left: "t0".to_string(),
5324 right: "t1".to_string(),
5325 result_type: None,
5326 },
5327 source_loc: None,
5328 },
5329 ANFBinding {
5330 name: "t3".to_string(),
5331 value: ANFValue::LoadProp { name: "target".to_string() },
5332 source_loc: None,
5333 },
5334 ANFBinding {
5335 name: "t4".to_string(),
5336 value: ANFValue::BinOp {
5337 op: "===".to_string(),
5338 left: "t2".to_string(),
5339 right: "t3".to_string(),
5340 result_type: None,
5341 },
5342 source_loc: None,
5343 },
5344 ANFBinding {
5345 name: "t5".to_string(),
5346 value: ANFValue::Assert { value: "t4".to_string() },
5347 source_loc: None,
5348 },
5349 ],
5350 is_public: true,
5351 }],
5352 };
5353
5354 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5355 let opcodes = collect_all_opcodes(&methods[0].ops);
5356
5357 assert!(
5359 opcodes.contains(&"OP_ADD".to_string()),
5360 "expected OP_ADD in stack ops for 'a + b', got: {:?}",
5361 opcodes
5362 );
5363
5364 assert!(
5366 opcodes.contains(&"OP_NUMEQUAL".to_string()),
5367 "expected OP_NUMEQUAL in stack ops for '===', got: {:?}",
5368 opcodes
5369 );
5370 }
5371
5372 #[test]
5378 fn test_s18_pick_roll_depth_within_max_stack_depth() {
5379 let program = p2pkh_program();
5380 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5381
5382 let max_depth = methods[0].max_stack_depth;
5383
5384 fn check_ops(ops: &[StackOp], max_depth: usize) {
5385 for op in ops {
5386 match op {
5387 StackOp::Pick { depth } => {
5388 assert!(
5389 *depth < max_depth,
5390 "Pick depth {} must be < max_stack_depth {}",
5391 depth,
5392 max_depth
5393 );
5394 }
5395 StackOp::Roll { depth } => {
5396 assert!(
5397 *depth < max_depth,
5398 "Roll depth {} must be < max_stack_depth {}",
5399 depth,
5400 max_depth
5401 );
5402 }
5403 StackOp::If { then_ops, else_ops } => {
5404 check_ops(then_ops, max_depth);
5405 check_ops(else_ops, max_depth);
5406 }
5407 _ => {}
5408 }
5409 }
5410 }
5411
5412 check_ops(&methods[0].ops, max_depth);
5413 }
5414
5415 #[test]
5421 fn test_bytestring_concat_emits_op_cat() {
5422 let program = ANFProgram {
5423 contract_name: "CatCheck".to_string(),
5424 properties: vec![],
5425 methods: vec![ANFMethod {
5426 name: "verify".to_string(),
5427 params: vec![
5428 ANFParam { name: "a".to_string(), param_type: "ByteString".to_string() },
5429 ANFParam { name: "b".to_string(), param_type: "ByteString".to_string() },
5430 ANFParam { name: "expected".to_string(), param_type: "ByteString".to_string() },
5431 ],
5432 body: vec![
5433 ANFBinding {
5434 name: "t0".to_string(),
5435 value: ANFValue::LoadParam { name: "a".to_string() },
5436 source_loc: None,
5437 },
5438 ANFBinding {
5439 name: "t1".to_string(),
5440 value: ANFValue::LoadParam { name: "b".to_string() },
5441 source_loc: None,
5442 },
5443 ANFBinding {
5444 name: "t2".to_string(),
5445 value: ANFValue::BinOp {
5446 op: "+".to_string(),
5447 left: "t0".to_string(),
5448 right: "t1".to_string(),
5449 result_type: Some("bytes".to_string()), },
5451 source_loc: None,
5452 },
5453 ANFBinding {
5454 name: "t3".to_string(),
5455 value: ANFValue::LoadParam { name: "expected".to_string() },
5456 source_loc: None,
5457 },
5458 ANFBinding {
5459 name: "t4".to_string(),
5460 value: ANFValue::BinOp {
5461 op: "===".to_string(),
5462 left: "t2".to_string(),
5463 right: "t3".to_string(),
5464 result_type: Some("bytes".to_string()),
5465 },
5466 source_loc: None,
5467 },
5468 ANFBinding {
5469 name: "t5".to_string(),
5470 value: ANFValue::Assert { value: "t4".to_string() },
5471 source_loc: None,
5472 },
5473 ],
5474 is_public: true,
5475 }],
5476 };
5477
5478 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5479 let opcodes = collect_all_opcodes(&methods[0].ops);
5480
5481 assert!(
5482 opcodes.contains(&"OP_CAT".to_string()),
5483 "ByteString '+' (result_type='bytes') should emit OP_CAT; got opcodes: {:?}",
5484 opcodes
5485 );
5486 assert!(
5487 !opcodes.contains(&"OP_ADD".to_string()),
5488 "ByteString '+' should NOT emit OP_ADD (that's for bigint); got opcodes: {:?}",
5489 opcodes
5490 );
5491 }
5492
5493 #[test]
5499 fn test_log2_emits_64_if_ops() {
5500 let program = ANFProgram {
5501 contract_name: "TestLog2Count".to_string(),
5502 properties: vec![],
5503 methods: vec![ANFMethod {
5504 name: "check".to_string(),
5505 params: vec![
5506 ANFParam { name: "n".to_string(), param_type: "bigint".to_string() },
5507 ],
5508 body: vec![
5509 ANFBinding {
5510 name: "t0".to_string(),
5511 value: ANFValue::LoadParam { name: "n".to_string() },
5512 source_loc: None,
5513 },
5514 ANFBinding {
5515 name: "t1".to_string(),
5516 value: ANFValue::Call {
5517 func: "log2".to_string(),
5518 args: vec!["t0".to_string()],
5519 },
5520 source_loc: None,
5521 },
5522 ANFBinding {
5523 name: "t2".to_string(),
5524 value: ANFValue::LoadConst {
5525 value: serde_json::Value::Number(serde_json::Number::from(0)),
5526 },
5527 source_loc: None,
5528 },
5529 ANFBinding {
5530 name: "t3".to_string(),
5531 value: ANFValue::BinOp {
5532 op: ">=".to_string(),
5533 left: "t1".to_string(),
5534 right: "t2".to_string(),
5535 result_type: None,
5536 },
5537 source_loc: None,
5538 },
5539 ANFBinding {
5540 name: "t4".to_string(),
5541 value: ANFValue::Assert { value: "t3".to_string() },
5542 source_loc: None,
5543 },
5544 ],
5545 is_public: true,
5546 }],
5547 };
5548
5549 let methods = lower_to_stack(&program).expect("stack lowering should succeed");
5550
5551 fn count_if_ops(ops: &[StackOp]) -> usize {
5553 let mut count = 0;
5554 for op in ops {
5555 match op {
5556 StackOp::If { then_ops, else_ops } => {
5557 count += 1;
5558 count += count_if_ops(then_ops);
5559 count += count_if_ops(else_ops);
5560 }
5561 _ => {}
5562 }
5563 }
5564 count
5565 }
5566
5567 let if_count = count_if_ops(&methods[0].ops);
5568 assert_eq!(
5569 if_count, 64,
5570 "log2 should emit exactly 64 if-ops (one per bit); got {} if-ops",
5571 if_count
5572 );
5573 }
5574}