ergotree_ir/
pretty_printer.rs

1//! Pretty printer for ErgoTree IR
2
3use std::fmt::Write;
4
5mod print;
6pub use print::Print;
7
8// TODO: extract to a separate module
9/// Printer trait with tracking of current position and indent
10pub trait Printer: Write {
11    /// Current position (last printed char)
12    fn current_pos(&self) -> usize;
13    /// Increase indent
14    fn inc_ident(&mut self);
15    /// Decrease indent
16    fn dec_ident(&mut self);
17    /// Get current indent
18    fn get_indent(&self) -> usize;
19    /// Print the current indent
20    fn print_indent(&mut self) -> std::fmt::Result {
21        write!(self, "{:indent$}", "", indent = self.get_indent())
22    }
23}
24
25/// Printer implementation with tracking of current position and indent
26pub struct PosTrackingWriter {
27    print_buf: String,
28    current_pos: usize,
29    current_indent: usize,
30}
31
32impl Write for PosTrackingWriter {
33    fn write_str(&mut self, s: &str) -> std::fmt::Result {
34        let len = s.len();
35        self.current_pos += len;
36        write!(self.print_buf, "{}", s)
37    }
38}
39
40impl Printer for PosTrackingWriter {
41    fn current_pos(&self) -> usize {
42        self.current_pos
43    }
44
45    fn inc_ident(&mut self) {
46        self.current_indent += Self::INDENT;
47    }
48
49    fn dec_ident(&mut self) {
50        self.current_indent -= Self::INDENT;
51    }
52
53    fn get_indent(&self) -> usize {
54        self.current_indent
55    }
56}
57
58impl PosTrackingWriter {
59    const INDENT: usize = 2;
60
61    /// Create new printer
62    pub fn new() -> Self {
63        Self {
64            print_buf: String::new(),
65            current_pos: 0,
66            current_indent: 0,
67        }
68    }
69
70    /// Get printed buffer
71    pub fn get_buf(&self) -> &str {
72        &self.print_buf
73    }
74
75    /// Get printed buffer as String
76    pub fn as_string(self) -> String {
77        self.print_buf
78    }
79}
80
81impl Default for PosTrackingWriter {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87#[allow(clippy::unwrap_used)]
88#[cfg(test)]
89mod tests {
90
91    use expect_test::expect;
92
93    use crate::chain::address::AddressEncoder;
94    use crate::chain::address::NetworkPrefix;
95    use crate::ergo_tree::ErgoTree;
96    use crate::mir::bin_op::ArithOp;
97    use crate::mir::bin_op::BinOp;
98    use crate::mir::block::BlockValue;
99    use crate::mir::expr::Expr;
100    use crate::mir::val_def::ValDef;
101    use crate::mir::val_use::ValUse;
102    use crate::serialization::SigmaSerializable;
103    use crate::types::stype::SType;
104
105    use super::*;
106
107    fn check_pretty(expr: Expr, expected_tree: expect_test::Expect) {
108        let print_buf = String::new();
109        let mut w = PosTrackingWriter {
110            print_buf,
111            current_pos: 0,
112            current_indent: 0,
113        };
114        let _ = expr.print(&mut w).unwrap();
115        expected_tree.assert_eq(w.get_buf());
116    }
117
118    fn check_spans(expr: Expr, expected_tree: expect_test::Expect) {
119        let print_buf = String::new();
120        let mut w = PosTrackingWriter {
121            print_buf,
122            current_pos: 0,
123            current_indent: 0,
124        };
125        let spanned_expr = expr.print(&mut w).unwrap();
126        expected_tree.assert_eq(format!("{:?}", spanned_expr).as_str());
127    }
128
129    #[test]
130    fn print_block() {
131        let val_id = 1.into();
132        let expr = Expr::BlockValue(
133            BlockValue {
134                items: vec![ValDef {
135                    id: val_id,
136                    rhs: Box::new(Expr::Const(1i32.into())),
137                }
138                .into()],
139                result: Box::new(
140                    ValUse {
141                        val_id,
142                        tpe: SType::SInt,
143                    }
144                    .into(),
145                ),
146            }
147            .into(),
148        );
149        check_pretty(
150            expr,
151            expect![[r#"
152            {
153              val v1 = 1
154              v1
155            }
156            "#]],
157        );
158    }
159
160    #[test]
161    fn print_binop() {
162        let val_id = 1.into();
163        let expr = Expr::BlockValue(
164            BlockValue {
165                items: vec![ValDef {
166                    id: val_id,
167                    rhs: Box::new(
168                        BinOp {
169                            kind: ArithOp::Divide.into(),
170                            left: Expr::Const(4i32.into()).into(),
171                            right: Expr::Const(2i32.into()).into(),
172                        }
173                        .into(),
174                    ),
175                }
176                .into()],
177                result: Box::new(
178                    ValUse {
179                        val_id,
180                        tpe: SType::SInt,
181                    }
182                    .into(),
183                ),
184            }
185            .into(),
186        );
187        check_pretty(
188            expr.clone(),
189            expect![[r#"
190            {
191              val v1 = 4 / 2
192              v1
193            }
194            "#]],
195        );
196
197        check_spans(
198            expr,
199            expect![[
200                r#"BlockValue(Spanned { source_span: SourceSpan { offset: 0, length: 26 }, expr: BlockValue { items: [ValDef(Spanned { source_span: SourceSpan { offset: 4, length: 14 }, expr: ValDef { id: ValId(1), rhs: BinOp(Spanned { source_span: SourceSpan { offset: 13, length: 5 }, expr: BinOp { kind: Arith(Divide), left: Const("4: SInt"), right: Const("2: SInt") } }) } })], result: ValUse(ValUse { val_id: ValId(1), tpe: SInt }) } })"#
201            ]],
202        );
203    }
204
205    #[test]
206    fn eip23_refresh_contract() {
207        let ergo_tree_bytes = base16::decode("1016043c040004000e202a472d4a614e645267556b58703273357638792f423f4528482b4d625065536801000502010105000400040004020402040204080400040a05c8010e20472b4b6250655368566d597133743677397a24432646294a404d635166546a570400040404020408d80ed60199a37300d602b2a4730100d603b5a4d901036395e6c672030605eded928cc77203017201938cb2db6308720373020001730393e4c672030504e4c6720205047304d604b17203d605b0720386027305860273067307d901053c413d0563d803d607e4c68c7205020605d6088c720501d6098c720802860272078602ed8c720901908c72080172079a8c7209027207d6068c720502d6078c720501d608db63087202d609b27208730800d60ab2a5730900d60bdb6308720ad60cb2720b730a00d60db27208730b00d60eb2a5730c00ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02cde4c6b27203e4e30004000407d18f8cc77202017201d1927204730dd18c720601d190997207e4c6b27203730e0006059d9c72077e730f057310d1938c7209017311d193b2720b7312007209d1938c720c018c720d01d1928c720c02998c720d027e9c7204731305d193b1720bb17208d193e4c6720a04059d8c7206027e720405d193e4c6720a05049ae4c6720205047314d193c2720ac27202d192c1720ac17202d1928cc7720a0199a37315d193db6308720edb6308a7d193c2720ec2a7d192c1720ec1a7").unwrap();
208        let ergo_tree = ErgoTree::sigma_parse_bytes(&ergo_tree_bytes).unwrap();
209        check_pretty(
210            ergo_tree.proposition().unwrap(),
211            expect![[r#"
212                {
213                  val v1 = HEIGHT - 30
214                  val v2 = INPUTS(0)
215                  val v3 = INPUTS.filter({
216                      (v3: Box) => 
217                        if (v3.getReg(6).isDefined()) v3.creationInfo._1 >= v1 && v3.tokens(0)._1 == "2a472d4a614e645267556b58703273357638792f423f4528482b4d6250655368" && v3.getReg(5).get == v2.getReg(5).get else false
218                      }
219                    )
220                  val v4 = v3.size
221                  val v5 = v3.fold((, 1, (, true, 0)))({
222                      (v5: ((Long, (Boolean, Long)), Box)) => 
223                        {
224                          val v7 = v5._2.getReg(6).get
225                          val v8 = v5._1
226                          val v9 = v8._2
227                          (, v7, (, v9._1 && v8._1 <= v7, v9._2 + v7))
228                        }
229
230                      }
231                    )
232                  val v6 = v5._2
233                  val v7 = v5._1
234                  val v8 = v2.tokens
235                  val v9 = v8(0)
236                  val v10 = OUTPUTS(0)
237                  val v11 = v10.tokens
238                  val v12 = v11(1)
239                  val v13 = v8(1)
240                  val v14 = OUTPUTS(1)
241                  allOf(
242                    allOf(
243                      allOf(
244                        allOf(
245                          allOf(
246                            allOf(
247                              allOf(
248                                allOf(
249                                  allOf(
250                                    allOf(
251                                      allOf(
252                                        allOf(
253                                          allOf(
254                                            allOf(
255                                              allOf(
256                                                allOf(
257                                                  allOf(
258                                                    proveDlog(v3(getVar(0).get).getReg(4).get), 
259                                                    sigmaProp(v2.creationInfo._1 < v1), 
260                                                  ), 
261                                                  sigmaProp(v4 >= 4), 
262                                                ), 
263                                                sigmaProp(v6._1), 
264                                              ), 
265                                              sigmaProp(v7 - v3(0).getReg(6).get <= v7 * upcast(5) / 100), 
266                                            ), 
267                                            sigmaProp(v9._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57"), 
268                                          ), 
269                                          sigmaProp(v11(0) == v9), 
270                                        ), 
271                                        sigmaProp(v12._1 == v13._1), 
272                                      ), 
273                                      sigmaProp(v12._2 >= v13._2 - upcast(v4 * 2)), 
274                                    ), 
275                                    sigmaProp(v11.size == v8.size), 
276                                  ), 
277                                  sigmaProp(v10.getReg(4).get == v6._2 / upcast(v4)), 
278                                ), 
279                                sigmaProp(v10.getReg(5).get == v2.getReg(5).get + 1), 
280                              ), 
281                              sigmaProp(v10.propBytes == v2.propBytes), 
282                            ), 
283                            sigmaProp(v10.value >= v2.value), 
284                          ), 
285                          sigmaProp(v10.creationInfo._1 >= HEIGHT - 4), 
286                        ), 
287                        sigmaProp(v14.tokens == SELF.tokens), 
288                      ), 
289                      sigmaProp(v14.propBytes == SELF.propBytes), 
290                    ), 
291                    sigmaProp(v14.value >= SELF.value), 
292                  )
293                }
294            "#]],
295        )
296    }
297
298    #[test]
299    fn eip23_update_contract() {
300        let ergo_tree_bytes = base16::decode("100f0400040004000402040204020e20472b4b6250655368566d597133743677397a24432646294a404d635166546a570400040004000e203f4428472d4b6150645367566b5970337336763979244226452948404d625165010005000400040cd80ad601b2a4730000d602db63087201d603b27202730100d604b2a5730200d605db63087204d606b2a5730300d607b27205730400d6088c720701d6098c720702d60ab27202730500d1ededed938c7203017306edededed937203b2720573070093c17201c1720493c672010405c67204040593c672010504c672040504efe6c672040661edededed93db63087206db6308a793c27206c2a792c17206c1a7918cc77206018cc7a701efe6c67206046192b0b5a4d9010b63d801d60ddb6308720b9591b1720d7308d801d60ec6720b070eededed938cb2720d73090001730a93e4c6720b05048cc7a70193e4c6720b060ecbc2720495ede6720ee6c6720b0805ed93e4720e720893e4c6720b08057209ed938c720a017208938c720a027209730b730cd9010b41639a8c720b018cb2db63088c720b02730d00027e730e05").unwrap();
301        let ergo_tree = ErgoTree::sigma_parse_bytes(&ergo_tree_bytes).unwrap();
302        check_pretty(
303            ergo_tree.proposition().unwrap(),
304            expect![[r#"
305                {
306                  val v1 = INPUTS(0)
307                  val v2 = v1.tokens
308                  val v3 = v2(0)
309                  val v4 = OUTPUTS(0)
310                  val v5 = v4.tokens
311                  val v6 = OUTPUTS(1)
312                  val v7 = v5(1)
313                  val v8 = v7._1
314                  val v9 = v7._2
315                  val v10 = v2(1)
316                  sigmaProp(v3._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57" && v3 == v5(0) && v1.value == v4.value && v1.getReg(4) == v4.getReg(4) && v1.getReg(5) == v4.getReg(5) && !v4.getReg(6).isDefined() && v6.tokens == SELF.tokens && v6.propBytes == SELF.propBytes && v6.value >= SELF.value && v6.creationInfo._1 > SELF.creationInfo._1 && !v6.getReg(4).isDefined() && INPUTS.filter({
317                      (v11: Box) => 
318                        {
319                          val v13 = v11.tokens
320                          if (v13.size > 0) {
321                            val v14 = v11.getReg(7)
322                            v13(0)._1 == "3f4428472d4b6150645367566b5970337336763979244226452948404d625165" && v11.getReg(5).get == SELF.creationInfo._1 && v11.getReg(6).get == blake2b256(v4.propBytes) && if (v14.isDefined() && v11.getReg(8).isDefined()) v14.get == v8 && v11.getReg(8).get == v9 else v10._1 == v8 && v10._2 == v9
323                          }
324                 else false
325                        }
326
327                      }
328                    ).fold(0)({
329                      (v11: (Long, Box)) => 
330                        v11._1 + v11._2.tokens(0)._2
331                      }
332                    ) >= upcast(6))
333                }
334            "#]],
335        )
336    }
337
338    #[test]
339    fn eip23_ballot_contract() {
340        let ergo_tree_bytes = base16::decode("10070580dac409040204020400040204000e206251655468576d5a7134743777217a25432a462d4a404e635266556a586e3272d803d601e4c6a70407d602b2a5e4e3000400d603c672020407eb02cd7201d1edededede6720393c27202c2a793db63087202db6308a792c172027300ededededed91b1a4730191b1db6308b2a47302007303938cb2db6308b2a473040073050001730693e47203720192c17202c1a7efe6c672020561").unwrap();
341        let ergo_tree = ErgoTree::sigma_parse_bytes(&ergo_tree_bytes).unwrap();
342        check_pretty(
343            ergo_tree.proposition().unwrap(),
344            expect![[r#"
345                {
346                  val v1 = SELF.getReg(4).get
347                  val v2 = OUTPUTS(getVar(0).get)
348                  val v3 = v2.getReg(4)
349                  anyOf(
350                    proveDlog(v1), 
351                    sigmaProp(v3.isDefined() && v2.propBytes == SELF.propBytes && v2.tokens == SELF.tokens && v2.value >= 10000000 && INPUTS.size > 1 && INPUTS(1).tokens.size > 0 && INPUTS(1).tokens(0)._1 == "6251655468576d5a7134743777217a25432a462d4a404e635266556a586e3272" && v3.get == v1 && v2.value >= SELF.value && !v2.getReg(5).isDefined()), 
352                  )
353                }
354            "#]],
355        )
356    }
357
358    #[test]
359    fn eip23_oracle_contract() {
360        let ergo_tree_bytes = base16::decode("100a040004000580dac409040004000e20472b4b6250655368566d597133743677397a24432646294a404d635166546a570402040204020402d804d601b2a5e4e3000400d602db63087201d603db6308a7d604e4c6a70407ea02d1ededed93b27202730000b2720373010093c27201c2a7e6c67201040792c172017302eb02cd7204d1ededededed938cb2db6308b2a4730300730400017305938cb27202730600018cb2720373070001918cb27202730800028cb272037309000293e4c672010407720492c17201c1a7efe6c672010561").unwrap();
361        let ergo_tree = ErgoTree::sigma_parse_bytes(&ergo_tree_bytes).unwrap();
362        check_pretty(
363            ergo_tree.proposition().unwrap(),
364            expect![[r#"
365                {
366                  val v1 = OUTPUTS(getVar(0).get)
367                  val v2 = v1.tokens
368                  val v3 = SELF.tokens
369                  val v4 = SELF.getReg(4).get
370                  allOf(
371                    sigmaProp(v2(0) == v3(0) && v1.propBytes == SELF.propBytes && v1.getReg(4).isDefined() && v1.value >= 10000000), 
372                    anyOf(
373                      proveDlog(v4), 
374                      sigmaProp(INPUTS(0).tokens(0)._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57" && v2(1)._1 == v3(1)._1 && v2(1)._2 > v3(1)._2 && v1.getReg(4).get == v4 && v1.value >= SELF.value && !v1.getReg(5).isDefined()), 
375                    ), 
376                  )
377                }
378            "#]],
379        )
380    }
381
382    #[test]
383    fn ageusd_bank_full() {
384        // from eip-15 https://github.com/ergoplatform/eips/pull/27/files
385        let p2s_addr_str = "MUbV38YgqHy7XbsoXWF5z7EZm524Ybdwe5p9WDrbhruZRtehkRPT92imXer2eTkjwPDfboa1pR3zb3deVKVq3H7Xt98qcTqLuSBSbHb7izzo5jphEpcnqyKJ2xhmpNPVvmtbdJNdvdopPrHHDBbAGGeW7XYTQwEeoRfosXzcDtiGgw97b2aqjTsNFmZk7khBEQywjYfmoDc9nUCJMZ3vbSspnYo3LarLe55mh2Np8MNJqUN9APA6XkhZCrTTDRZb1B4krgFY1sVMswg2ceqguZRvC9pqt3tUUxmSnB24N6dowfVJKhLXwHPbrkHViBv1AKAJTmEaQW2DN1fRmD9ypXxZk8GXmYtxTtrj3BiunQ4qzUCu1eGzxSREjpkFSi2ATLSSDqUwxtRz639sHM6Lav4axoJNPCHbY8pvuBKUxgnGRex8LEGM8DeEJwaJCaoy8dBw9Lz49nq5mSsXLeoC4xpTUmp47Bh7GAZtwkaNreCu74m9rcZ8Di4w1cmdsiK1NWuDh9pJ2Bv7u3EfcurHFVqCkT3P86JUbKnXeNxCypfrWsFuYNKYqmjsix82g9vWcGMmAcu5nagxD4iET86iE2tMMfZZ5vqZNvntQswJyQqv2Wc6MTh4jQx1q2qJZCQe4QdEK63meTGbZNNKMctHQbp3gRkZYNrBtxQyVtNLR8xEY8zGp85GeQKbb37vqLXxRpGiigAdMe3XZA4hhYPmAAU5hpSMYaRAjtvvMT3bNiHRACGrfjvSsEG9G2zY5in2YWz5X9zXQLGTYRsQ4uNFkYoQRCBdjNxGv6R58Xq74zCgt19TxYZ87gPWxkXpWwTaHogG1eps8WXt8QzwJ9rVx6Vu9a5GjtcGsQxHovWmYixgBU8X9fPNJ9UQhYyAWbjtRSuVBtDAmoV1gCBEPwnYVP5GCGhCocbwoYhZkZjFZy6ws4uxVLid3FxuvhWvQrVEDYp7WRvGXbNdCbcSXnbeTrPMey1WPaXX";
386        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
387        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
388        let expr = addr.script().unwrap().proposition().unwrap();
389        check_pretty(
390            expr,
391            expect![[r#"
392                {
393                  val v1 = CONTEXT.dataInputs
394                  sigmaProp(if (v1.size > 0) {
395                    val v2 = v1(0)
396                    val v3 = v2.tokens(0)._1 == "011d3364de07e5a26f0c4eef0852cddb387039a921b7154ef3cab22c6eda887f"
397                    val v4 = OUTPUTS(0)
398                    val v5 = v4.value
399                    val v6 = SELF.tokens
400                    val v7 = v6(1)
401                    val v8 = v7._2
402                    val v9 = v4.tokens
403                    val v10 = v9(1)
404                    val v11 = v10._2
405                    val v12 = v8 != v11
406                    val v13 = v6(0)
407                    val v14 = v13._2
408                    val v15 = v9(0)
409                    val v16 = v15._2
410                    val v17 = v14 != v16
411                    val v18 = SELF.getReg(5).get
412                    val v19 = v4.getReg(5).get
413                    val v20 = SELF.getReg(4).get
414                    val v21 = v4.getReg(4).get
415                    val v22 = OUTPUTS(1)
416                    val v23 = v22.getReg(4).get
417                    val v24 = if (v12) 0 else v23
418                    val v25 = if (v12) v23 else 0
419                    val v26 = SELF.value
420                    val v27 = v22.getReg(5).get
421                    val v28 = v2.getReg(4).get / 100
422                    val v29 = v26 min v20 * v28 max 0
423                    val v30 = if (v17) v28 min if (v20 == 0) 9223372036854775807 else v29 / v20 * v24 else {
424                      val v30 = v26 - v29
425                      if (v30 == 0) 1000000 else if (v18 == 0) 1000000 else v30 / v18 * v25
426                    }
427
428                    val v31 = v30 * upcast(2) / 100
429                    val v32 = v21 * v28
430                    val v33 = if (HEIGHT > 460000) 800 else 1000000000
431                    val v34 = if (v32 == 0) v33 else v5 * 100 / v32
432                    v3 && v5 >= 10000000 && v4.propBytes == SELF.propBytes && v12 || v17 && !v12 && v17 && v8 + v18 == v11 + v19 && v14 + v20 == v16 + v21 && v20 + v24 == v21 && v18 + v25 == v19 && v26 + v27 == v5 && v21 >= 0 && v19 >= 0 && v15._1 == v13._1 && v10._1 == v7._1 && v9(2)._1 == v6(2)._1 && v27 == v30 + if (v31 < 0) -v31 else v31 && if (v17) if (v24 > 0) v34 >= 400 else true else if (v25 > 0) v34 <= v33 else v34 >= 400 && v3
433                  }
434                 else false || INPUTS(0).tokens(0)._1 == "239c170b7e82f94e6b05416f14b8a2a57e0bfff0e3c93f4abbcd160b6a5b271a")
435                }
436            "#]],
437        )
438    }
439
440    #[test]
441    fn ageusd_update() {
442        // from eip-15 https://github.com/ergoplatform/eips/pull/27/files
443        let p2s_addr_str = "VLyjpv3dse3PbatT83GnDkBQasGqY52dAEdi9XpXhuSUn1FS1Tm7XxtAgmBiqY9pJXtEAsDKwX9ygSjrFu7vnUQZudhC2sSmxhxqgD3ZxJ2VsGwmPG77F6EiEZhcq71oqEq31y9XvCCXL5nqqszdENPAVhu7xT296qZ7w1x6hmwdh9ZE89bjfgbhfNYopoqsCaNLWYHJ12TDSY93kaGqCVKSu6gEF1gLpXBfRCnAPPxYswJPmK8oWDn8PKrUGs3MjVsj6bGXiW3VTGP4VsNH8YSSkjyj1FZ9azLsyfnNJ3zah2zUHdCCqY6PjH9JfHf9joCPf6TusvXgr71XWvh5e2HPEPQr4eJMD4S96cGTiSs3J5XcRd1tCDYoiis8nxv99zFFhHgpqXHgeqjhJ5sPot9eRYTsmm4cRTVLXYAiuKPS2qW5";
444        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
445        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
446        let expr = addr.script().unwrap().proposition().unwrap();
447        check_pretty(
448            expr,
449            expect![[r#"
450                {
451                  val v1 = INPUTS(1)
452                  val v2 = v1.tokens
453                  val v3 = OUTPUTS(1)
454                  val v4 = SELF.id
455                  val v5 = OUTPUTS(0)
456                  sigmaProp(v2.size == 3 && v2(2)._1 == "7d672d1def471720ca5782fd6473e47e796d9ac0c138d9911346f118b2f6d9d9" && v2 == v3.tokens && v1.value == v3.value && v1.getReg(4).get == v3.getReg(4).get && v1.getReg(5).get == v3.getReg(5).get && v4 == INPUTS(0).id && SELF.tokens == v5.tokens && SELF.propBytes == v5.propBytes && v5.value >= SELF.value && INPUTS.filter({
457                      (v6: Box) => 
458                        {
459                          val v8 = v6.tokens
460                          v8.size > 0 && v8(0)._1 == "f7995f212216fcf21854f56df7a9a0a9fc9b7ae4c0f1cc40f5b406371286a5e0" && v6.getReg(6).get == v4 && v6.getReg(7).get == blake2b256(v3.propBytes)
461                        }
462
463                      }
464                    ).fold(0)({
465                      (v6: (Long, Box)) => 
466                        v6._1 + v6._2.tokens(0)._2
467                      }
468                    ) >= upcast(3))
469                }
470            "#]],
471        )
472    }
473
474    #[test]
475    fn ageusd_ballot() {
476        // from eip-15 https://github.com/ergoplatform/eips/pull/27/files
477        let p2s_addr_str = "22ELWBHzyWGjPRE48ZJDfFmD24myYdG3vHz8CipSS7rgE65ABmEj9QJiy3rG2PTJeCaZw9VX56GY6uoA3hQch7i5BfFU3AprUWTABi4X1VWtRdK9yrYJkmN6fq8hGfvmWTrsyh4fXZoGETpLuXQViYo194ajej2h7dr3oqNATdMskSXzxJi83bFdAvQ";
478        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
479        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
480        let expr = addr.script().unwrap().proposition().unwrap();
481        check_pretty(
482            expr,
483            expect![[r#"
484                {
485                  val v1 = OUTPUTS(INPUTS.indexOf(SELF0))
486                  val v2 = SELF.getReg(4).get
487                  allOf(
488                    sigmaProp(v1.getReg(4).get == v2 && v1.propBytes == SELF.propBytes && v1.tokens == SELF.tokens && v1.value >= SELF.value), 
489                    anyOf(
490                      proveDlog(v2), 
491                      sigmaProp(INPUTS(0).tokens(0)._1 == "239c170b7e82f94e6b05416f14b8a2a57e0bfff0e3c93f4abbcd160b6a5b271a" && !v1.getReg(7).isDefined()), 
492                    ), 
493                  )
494                }
495            "#]],
496        )
497    }
498
499    #[test]
500    fn amm_simple_pool() {
501        // from eip-14 https://github.com/ergoplatform/eips/pull/27/files
502        let p2s_addr_str = "k6fD5ht5e1itDejPFV2VzAoHv478KQCbDnLAL6XUVeEu8KDaboCVZAoFz2AtMoLqM3CgQfr2TZhpwz7K96AgwTXDvBVeTchJ31jjD46Di1W67H8wwFcivnY62UB6L7HWzCkbYuiZaAq2qSJta5Twt4A2Aaoy7xViWcyLUVNAyQYDJXKhVBAGwp76i2too5yWUmEU4zt9XnjJAUt1FFfurNtTNHNPDbqmTRE4crz347q6rfbvkMmg9Jtk9rSiPCQpKjdbZVzUnP4CUw6AvQH6rZXxgNMktAtjQdHhCnrCmf78FwCKqYS54asKd1MFgYNT4NzPwmdZF6JtQt1vvkjZXqpGkjy33xxDNYy8JZS8eeqVgZErPeJ1aj4aaK8gvmApUgGStMDFeFYjuQqZiZxEAHNdAXDg7hyGnmfzA6Hj9zcB7p9nKCDNhEQEMPL1kMG5aXvt2HUPXqiCkLrv596DaGmRMN3gMJaj1T1AfMYNwZozcJ9uUSK4i6Xham28HWAekTtDPhobnmjvkubwLVTtvUumWHtDWFxYSJPF7vqzgZqg6Y5unMF";
503        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
504        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
505        let expr = addr.script().unwrap().proposition().unwrap();
506        check_pretty(
507            expr,
508            expect![[r#"
509                {
510                  val v1 = OUTPUTS(0)
511                  val v2 = v1.tokens
512                  val v3 = SELF.tokens
513                  val v4 = v2(0)
514                  val v5 = v3(0)
515                  val v6 = v2(2)
516                  val v7 = v3(2)
517                  val v8 = v2(3)
518                  val v9 = v3(3)
519                  val v10 = 1000000000000000000 - v5._2
520                  val v11 = 1000000000000000000 - v4._2 - v10
521                  val v12 = v7._2
522                  val v13 = v6._2 - v12
523                  val v14 = v13 > 0
524                  val v15 = v9._2
525                  val v16 = upcast(v15)
526                  val v17 = upcast(v13)
527                  val v18 = v8._2 - v15
528                  val v19 = upcast(v12)
529                  val v20 = upcast(v18)
530                  val v21 = upcast(v10)
531                  val v22 = upcast(v11) / v21
532                  sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v2(1) == v3(1) && v4._1 == v5._1 && v6._1 == v7._1 && v8._1 == v9._1 && if (v11 == 0) if (v14) v16 * v17 * BigInt256(Int256(997)) >= upcast(-v18) * v19 * BigInt256(Int256(1000)) + upcast(v13 * 997) else v19 * v20 * BigInt256(Int256(997)) >= upcast(-v13) * v16 * BigInt256(Int256(1000)) + upcast(v18 * 997) else if (v14 && v18 > 0) upcast(-v11) <= v17 * v21 / v19 min v20 * v21 / v16 else v17 >= v22 * v19 && v20 >= v22 * v16)
533                }
534            "#]],
535        )
536    }
537
538    #[test]
539    fn amm_simple_swap() {
540        // from eip-14 https://github.com/ergoplatform/eips/pull/27/files
541        let p2s_addr_str = "cLPHJ3MHuKAHoCUwGhcEFw5sWJqvPwFyKxTRj1aUoMwgAz78Fg3zLXRhBup9Te1WLau1gZXNmXvUmeXGCd7QLeqB7ArrT3v5cg26piEtqymM6j2SkgYVCobgoAGKeTf6nMLxv1uVrLdjt1GnPxG1MuWj7Es7Dfumotbx9YEaxwqtTUC5SKsJc9LCpAmNWRAQbU6tVVEvmfwWivrGoZ3L5C4DMisxN3U";
542        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
543        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
544        let expr = addr.script().unwrap().proposition().unwrap();
545        check_pretty(
546            expr,
547            expect![[r#"
548                {
549                  val v1 = INPUTS(0).tokens
550                  val v2 = v1(2)
551                  val v3 = SELF.tokens(0)
552                  val v4 = v3._1
553                  val v5 = v1(3)
554                  val v6 = v3._2
555                  sigmaProp(v2._1 == v4 || v5._1 == v4 && OUTPUTS.exists({
556                      (v7: Box) => 
557                        {
558                          val v9 = v7.tokens(0)._2
559                          v9 >= upcast(1000) && upcast(v5._2) * upcast(v6) * BigInt256(Int256(997)) <= upcast(v9) * upcast(v2._2) * BigInt256(Int256(1000)) + upcast(v6 * 997)
560                        }
561
562                      }
563                ))
564                  }
565            "#]],
566        )
567    }
568
569    #[test]
570    fn amm_conc_pool_root() {
571        // from eip-14 https://github.com/ergoplatform/eips/pull/27/files
572        let p2s_addr_str = "3STRfQWC9Xb5wAxBiEQ74uTFSemk1oHn43mwj9tMCeu2a3A4kie1bY2qsCdRaEmdQoq3B4tXQuzq9nm84A8PmBgCzgGDEZf2pgYoAUc6krZxUY3rvKWW44ZpzN3u5bFRpKDo6rxKtxX2tw99xmfyfaVBejgDaTfsib2PSVsu9hrLQ3SouECWHQMjDA3Pi8ZuCvQeW8GDkZfHPr3SgwaxY1jpY2njsmf3JBASMoVZ6Mfpg63Q6mBno7mKUSCE7vNHHUZe2V7JEikwjPkaxSWxnwy3J17faGtiEHZLKiNQ9WNtsJLbdVp56dQGfC2zaiXjhx1XJK6m4Nh2M8yEvSuBzanRBAJqrNseGS97tk2iLqqfHrqqmmDsHY3mujCURky4SLr7YLk4B";
573        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
574        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
575        let expr = addr.script().unwrap().proposition().unwrap();
576        check_pretty(
577            expr,
578            expect![[r#"
579                {
580                  val v1 = OUTPUTS(0)
581                  val v2 = SELF.tokens(0)
582                  val v3 = v2._1
583                  val v4 = SELF.getReg(4).get
584                  val v5 = SELF.getReg(5).get
585                  val v6 = SELF.getReg(6).get
586                  val v7 = OUTPUTS(1)
587                  val v8 = v7.tokens
588                  val v9 = v7.getReg(6).get
589                  val v10 = v5._2
590                  val v11 = v5._1
591                  val v12 = v7.getReg(5).get
592                  val v13 = v7.getReg(7).get
593                  sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v1.tokens(0) == (, v3, v2._2 - 1) && v1.getReg(4).get == v4 && v1.getReg(5).get == v5 && v1.getReg(6).get == v6 && v7.getReg(4).get == v4 && v7.getReg(8).get == v6 && v8(1)._1 == v3 && v8(0) == (, SELF.id, 1000000000000000000) && v9._1 * v10 == v9._2 * v11 * v12 && v13._1 * v10 == v13._2 * v11 * v12 + 1)
594                }
595            "#]],
596        )
597    }
598
599    #[test]
600    fn amm_conc_pool_boot() {
601        // from eip-14 https://github.com/ergoplatform/eips/pull/27/files
602        let p2s_addr_str = "6Mv73vd1MnJp6AQg5vHGP9nujFc3Y1PL5gzeUt9PzCaUiQug7ueQGU1bDkmFkCspq4LU8j3T8yY6UyJQKSfah5qEDzjx8QCJF47NBG5jxgPxmBHkM6cUgnYa5ngzn9jrpAn379UC7o5nugTg3HYWZGk3APMcRftkrC3EgroiVMEmSkDcDwaebkNWKfKe3JXgewoTrgZ2YLMafr3JfX47C1zddoWDhS8TWryQYEprkP334eisuh1Fr2iNTW9ruV6m38cRkfRfzSBHYq45mvNLH7JQo6uQZ4NFPx4t27Q5A3mSqCpk7ATThFcQmc2w3Pp2F6xL87c94gxk83G8UEqkAhmaNfoj19zji9rxqRzq9gJeTLBraHR2DchKtahH8HhFPg5DZ4SjwJ4MHqTDF";
603        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
604        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
605        let expr = addr.script().unwrap().proposition().unwrap();
606        check_pretty(
607            expr,
608            expect![[r#"
609                {
610                  val v1 = OUTPUTS(0)
611                  val v2 = v1.tokens
612                  val v3 = SELF.tokens
613                  val v4 = v1.getReg(5).get
614                  val v5 = v1.getReg(6).get
615                  val v6 = v2(3)
616                  val v7 = v6._2
617                  val v8 = upcast(v7)
618                  val v9 = v1.getReg(7).get
619                  val v10 = upcast(v9)
620                  val v11 = v2(2)
621                  val v12 = v3(0)
622                  sigmaProp(true && v1.value >= SELF.value && v2(0) == (, SELF.id, 1) && v2(1) == (, v3(1)._1, 1) && v1.getReg(4).get == SELF.getReg(4).get && v4 == SELF.getReg(6).get && v5 == SELF.getReg(7).get && (, v6._1, v2(4)._1) == SELF.getReg(8).get && v8 * v8 == v10 * v10 && if (v11._1 == v12._1) v11._2 else 0 >= v12._2 - v9 && v7 * upcast(v4._2) >= v7 * upcast(v4._1) && v7 * upcast(v5._2) < v7 * upcast(v5._1))
623                }
624            "#]],
625        )
626    }
627
628    #[test]
629    fn amm_conc_pool() {
630        // from eip-14 https://github.com/ergoplatform/eips/pull/27/files
631        let p2s_addr_str = "AhCu1UkNT4c9q3B2Lb7gNgvZWCdXL8iYgmNxTYiy4S3wgKWFFW6kz9v7pvY8NqC7g4wgXXwzJY1fQVn2xrLkiyiQWsorq5dR7d5KnDAY43H4GvSVjaDciadXCSHCb8jgk8mFSQCwoZHweLmMJ25312wT85AySJgYUuzdUxMz4EnQpiwZR2XVZq3M81gycuqP9gUryryjN4J1cAF3yL3kZR3rREubBvJ2CY5hF74Xaj2jwajivkESkqq22ieWWG2sK7dk1A7KHr1MmiXGcUBAMMGPAu3mVCeFW9SongxP9hodnJThLknjWRBBBC6wq5jNkSdHrMbdaQM3XesXqGTk9KwWpnSL92E96muU2k8FQbo5isps1r5ciYVrFptfEAC3tWbwcVmRKtrgxtCex6bP5aBZYjaH6L9QQbkYriDAcQ1iZcpf3hHCqURjRXL7i72C3aGBwzzspQvhLof6x4f4gPxTCtF1bNUxddUL6DJ1PbQWzVH8taivjhHohis6sRn3Akvv4xaZRJdKZ8rDuiounRKNXi8VoNgVEZbSFYtfweRSdsiXJCkhtehLWdtFTk1eg7djASdBGKaguvtEBcGaAALVDUoH479VskPUQ6hrfS7KcWrATBdb8sf4W5MFpx7UNitzq2fzSKC96mQRUzy5uELe7Y7vexm5ArNEyr6ARkypZypSzJ2CEifjVxxRBEWVtbdqHrwP4gWv6cMdbqFWwuXAw2BZQnWpZFtKAGQ9m";
632        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
633        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
634        let expr = addr.script().unwrap().proposition().unwrap();
635        check_pretty(
636            expr,
637            expect![[r#"
638                {
639                  val v1 = OUTPUTS(0)
640                  val v2 = v1.tokens
641                  val v3 = SELF.tokens
642                  val v4 = v2(2)
643                  val v5 = v3(2)
644                  val v6 = v2(3)
645                  val v7 = v3(3)
646                  val v8 = v2(4)
647                  val v9 = v3(4)
648                  val v10 = SELF.getReg(4).get
649                  val v11 = SELF.getReg(5).get
650                  val v12 = SELF.getReg(6).get
651                  val v13 = 1000000000000000000 - v5._2
652                  val v14 = 1000000000000000000 - v4._2 - v13
653                  val v15 = v6._2
654                  val v16 = upcast(v15)
655                  val v17 = v8._2
656                  val v18 = upcast(v17)
657                  val v19 = v16 * upcast(v11._2) >= v18 * upcast(v11._1) && v16 * upcast(v12._2) < v18 * upcast(v12._1)
658                  val v20 = v7._2
659                  val v21 = v15 - v20
660                  val v22 = v21 > 0
661                  val v23 = v9._2
662                  val v24 = upcast(v23)
663                  val v25 = upcast(v21)
664                  val v26 = v17 - v23
665                  val v27 = upcast(v20)
666                  val v28 = 1000
667                  val v29 = upcast(v26)
668                  val v30 = upcast(v13)
669                  val v31 = upcast(v14) / v30
670                  sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v2(0) == v3(0) && v2(1) == v3(1) && v4._1 == v5._1 && v6._1 == v7._1 && v8._1 == v9._1 && v1.getReg(4).get == v10 && v1.getReg(5).get == v11 && v1.getReg(6).get == v12 && if (v14 == 0) if (v22) v24 * v25 * upcast(v10) >= upcast(-v26) * v27 * upcast(v28) + upcast(v21 * upcast(v10)) else v27 * v29 * upcast(v10) >= upcast(-v21) * v24 * upcast(v28) + upcast(v26 * upcast(v10)) && v19 else if (v22 && v26 > 0) upcast(-v14) <= v25 * v30 / v27 min v29 * v30 / v24 && v19 else v25 >= v31 * v27 && v29 >= v31 * v24)
671                }
672            "#]],
673        )
674    }
675
676    #[test]
677    fn eip_22_auction() {
678        // from https://github.com/ergoplatform/eips/blob/adbe21512cadf51a2d9af8406cfd418f95335899/eip-0022.md
679        let p2s_addr_str = "GE68RH3VEgW6b4kN3GhYigrLxoXr9jMgMpmm3KnXJaYq1PzHieYhz7Uka86idxvBWLRLmpiA3HrxHPsX1jzQZEv5yaRDueiJqks1keM7iB1eYWMEVRUUq1MLFzdA1FHQwCfSakM3Uc8uBPqk2icxhoXvw1CVbUVtFCzcPrZzf8Jjf8gS5bCFpWQscHo14HTsdBxyV3dwL6wKu8LP8FuWJE7qCEgX9ToEiztH4ZLmFwBejnUFrCQqjLVLWpdgdnAXVyewiX9DxXKJKL4wNqhPUrYjmHEVvpZAezXjzfVMr7gKupTqAgx2AJYGh4winEDeYq9MVshX8xjJweGhbAm2RXN1euQpoepFaKqfrT2mQBTmr6edbbzYg6VJ7DoSCDzmcUupFAmZMjMiaUbgtyz2VEbPEKsmAFrZ6zdB5EUxhiYZMd6KdstsJwZCgKJSSCShTgpfqNLCdpR9JbZFQpA1uhUkuLMPvGi74V5EwijTEEtjmTVcWcVhJKv4GDr1Lqe2bMPq4jfEfqvemaY8FcrCsCSi2LZoQUeJ9VrBeotGTKccq8JhwnvNGhLUUrrm32v3bhU82jbtVBVFRD3FSv5hhS6pKHtTevjwuG7JWoR3LN7279A7zQGJWmkSWDoEhHjgxseqZ2G5bLB7ZVEzKM261QhwMwmXA1eWgq8zdBH1u9kFC9bMQ812q2DPZTuhzpBWJh74UGwaEgZLhnUrDKT58cEa4R3kfWyGCMoNw78q1E3a2eKDz8Va5wnixzT2SZFHU8DfHjPSz5rm8Mr3YxgRC6GzaasPDxTrZjuMJHU2exhqsoFvur7Q";
680        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
681        let addr = encoder.parse_address_from_str(p2s_addr_str).unwrap();
682        let expr = addr.script().unwrap().proposition().unwrap();
683        check_pretty(
684            expr,
685            expect![[r#"
686                {
687                  val v1 = OUTPUTS(0)
688                  val v2 = CONTEXT.preHeader.timestamp
689                  val v3 = SELF.getReg(7).get
690                  val v4 = SELF.tokens
691                  val v5 = v4.size
692                  val v6 = v5 == 1
693                  val v7 = {
694                      (v7: Box) => 
695                        if (v6) v7.value else v7.tokens(1)._2
696                      }
697
698                    val v8 = v7(SELF)
699                    val v9 = SELF.getReg(6).get
700                    val v10 = SELF.getReg(8).get
701                    val v11 = Coll[Coll[Byte]]()
702                    val v12 = OUTPUTS(1)
703                    val v13 = SELF.getReg(5).get
704                    val v14 = SELF.getReg(4).get
705                    val v15 = CONTEXT.dataInputs(0)
706                    val v16 = v15.getReg(8).get
707                    sigmaProp(v1.value >= SELF.value && v2 < v3 && v1.tokens(0) == v4(0) && {
708                      val v17 = v7(v1)
709                      v17 >= v8 + v9 || v10 != -1 && v17 >= v10
710                    }
711                 && v1.propBytes == SELF.propBytes && v5 == v1.tokens.size && {
712                        (v17: Box) => 
713                          if (v6) v11 else v17.tokens(1)._1
714                        }
715                (SELF) == {
716                          (v17: Box) => 
717                            if (v6) v11 else v17.tokens(1)._1
718                          }
719                (v1) && v12.propBytes == v13 && v7(v12) >= v8 && v1.getReg(4).get == v14 && v1.getReg(5).get.size > 0 && v1.getReg(6).get == v9 && v1.getReg(7).get == if (v3 - v2 <= v16(0)) v3 + v16(1) else v3 && v1.getReg(8).get == v10 && v1.getReg(9) == SELF.getReg(9) || if (OUTPUTS.size == 5) {
720                          val v17 = OUTPUTS(2)
721                          val v18 = v8 / upcast(v15.getReg(4).get)
722                          val v19 = v4(0)
723                          val v20 = v8 / upcast(v15.getReg(6).get)
724                          val v21 = OUTPUTS(3)
725                          val v22 = v21.getReg(4).get
726                          v2 >= v3 || v8 >= v10 && v10 != -1 && v7(v17) >= v18 && v17.propBytes == v15.getReg(5).get && v1.tokens(0) == v19 && v1.propBytes == v13 && v7(v12) >= v8 - v18 - v20 - if (v6) v15.getReg(7).get * 2 else 0 && v12.propBytes == v14 && blake2b256(v22.bytes) == v19._1 && v7(v21) >= v20 && v21.propBytes == v22.propBytes
727                        }
728                 else false)
729                      }
730            "#]],
731        )
732    }
733}