Skip to main content

clvm_fuzzing/
make_tree.rs

1use arbitrary::{Arbitrary, Result, Unstructured};
2use chia_bls::{G1Element, G2Element};
3use clvmr::{Allocator, NodePtr, SExp};
4
5enum Op {
6    Pair(bool),
7    SubTree,
8}
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
11pub enum ValueKind {
12    Program,
13    Bytes32,
14    SmallInt,
15    G1Point,
16    G2Point,
17    LargeBytes,
18    EnvPath,
19    Tree,
20    Bool,
21    List,
22}
23
24#[derive(Clone, Copy, Debug)]
25pub enum Arity {
26    Nullary,
27    Unary(ValueKind),
28    Binary(ValueKind, ValueKind),
29    Ternary(ValueKind, ValueKind, ValueKind),
30    BinaryOrTernary(ValueKind, ValueKind, ValueKind),
31    Quaternary(ValueKind, ValueKind, ValueKind, ValueKind),
32    Varargs {
33        min: usize,
34        max: usize,
35        arg: ValueKind,
36    },
37    BlsVerify {
38        max_pairs: usize,
39        sig: ValueKind,
40        pk: ValueKind,
41        msg: ValueKind,
42    },
43    BlsPairingIdentity {
44        max_pairs: usize,
45        g1: ValueKind,
46        g2: ValueKind,
47    },
48}
49
50#[derive(Clone, Copy, Debug)]
51struct OpEntry {
52    opcode: u32,
53    arity: Arity,
54    return_kind: ValueKind,
55}
56
57const VARARGS_MAX: usize = 8;
58const MAX_BLS_VERIFY_PAIRS: usize = 4;
59const MAX_BLS_PAIRING_PAIRS: usize = 4;
60
61const PROGRAM_OPS: &[OpEntry] = &[
62    OpEntry {
63        opcode: 2,
64        arity: Arity::Binary(ValueKind::Program, ValueKind::List), // apply
65        return_kind: ValueKind::Tree,
66    },
67    OpEntry {
68        opcode: 3,
69        arity: Arity::Ternary(ValueKind::Bool, ValueKind::Program, ValueKind::Program), // if
70        return_kind: ValueKind::Program,
71    },
72    OpEntry {
73        opcode: 4,
74        arity: Arity::Binary(ValueKind::Tree, ValueKind::Tree), // cons
75        return_kind: ValueKind::List,
76    },
77    OpEntry {
78        opcode: 5,
79        arity: Arity::Unary(ValueKind::List), // first
80        return_kind: ValueKind::Tree,
81    },
82    OpEntry {
83        opcode: 6,
84        arity: Arity::Unary(ValueKind::List), // rest
85        return_kind: ValueKind::List,
86    },
87    OpEntry {
88        opcode: 7,
89        arity: Arity::Unary(ValueKind::Tree), // listp
90        return_kind: ValueKind::Bool,
91    },
92    OpEntry {
93        opcode: 8,
94        arity: Arity::Unary(ValueKind::Tree), // raise
95        return_kind: ValueKind::Tree,
96    },
97    OpEntry {
98        opcode: 9,
99        arity: Arity::Binary(ValueKind::LargeBytes, ValueKind::LargeBytes), // eq
100        return_kind: ValueKind::Bool,
101    },
102    OpEntry {
103        opcode: 10,
104        arity: Arity::Binary(ValueKind::LargeBytes, ValueKind::LargeBytes), // gr_bytes
105        return_kind: ValueKind::Bool,
106    },
107    OpEntry {
108        opcode: 11,
109        arity: Arity::Varargs {
110            min: 0,
111            max: VARARGS_MAX,
112            arg: ValueKind::LargeBytes,
113        }, // sha256
114        return_kind: ValueKind::Bytes32,
115    },
116    OpEntry {
117        opcode: 12,
118        arity: Arity::BinaryOrTernary(
119            ValueKind::LargeBytes,
120            ValueKind::SmallInt,
121            ValueKind::SmallInt,
122        ), // substr
123        return_kind: ValueKind::LargeBytes,
124    },
125    OpEntry {
126        opcode: 13,
127        arity: Arity::Unary(ValueKind::LargeBytes), // strlen
128        return_kind: ValueKind::SmallInt,
129    },
130    OpEntry {
131        opcode: 14,
132        arity: Arity::Varargs {
133            min: 0,
134            max: VARARGS_MAX,
135            arg: ValueKind::LargeBytes,
136        }, // concat
137        return_kind: ValueKind::LargeBytes,
138    },
139    OpEntry {
140        opcode: 16,
141        arity: Arity::Varargs {
142            min: 0,
143            max: VARARGS_MAX,
144            arg: ValueKind::SmallInt,
145        }, // add
146        return_kind: ValueKind::SmallInt,
147    },
148    OpEntry {
149        opcode: 17,
150        arity: Arity::Varargs {
151            min: 0,
152            max: VARARGS_MAX,
153            arg: ValueKind::SmallInt,
154        }, // subtract
155        return_kind: ValueKind::SmallInt,
156    },
157    OpEntry {
158        opcode: 18,
159        arity: Arity::Varargs {
160            min: 0,
161            max: VARARGS_MAX,
162            arg: ValueKind::SmallInt,
163        }, // multiply
164        return_kind: ValueKind::SmallInt,
165    },
166    OpEntry {
167        opcode: 19,
168        arity: Arity::Binary(ValueKind::SmallInt, ValueKind::SmallInt), // div
169        return_kind: ValueKind::SmallInt,
170    },
171    OpEntry {
172        opcode: 20,
173        arity: Arity::Binary(ValueKind::SmallInt, ValueKind::SmallInt), // divmod
174        return_kind: ValueKind::List,
175    },
176    OpEntry {
177        opcode: 21,
178        arity: Arity::Binary(ValueKind::SmallInt, ValueKind::SmallInt), // gr
179        return_kind: ValueKind::Bool,
180    },
181    OpEntry {
182        opcode: 22,
183        arity: Arity::Binary(ValueKind::SmallInt, ValueKind::SmallInt), // ash
184        return_kind: ValueKind::SmallInt,
185    },
186    OpEntry {
187        opcode: 23,
188        arity: Arity::Binary(ValueKind::SmallInt, ValueKind::SmallInt), // lsh
189        return_kind: ValueKind::SmallInt,
190    },
191    OpEntry {
192        opcode: 24,
193        arity: Arity::Varargs {
194            min: 0,
195            max: VARARGS_MAX,
196            arg: ValueKind::SmallInt,
197        }, // logand
198        return_kind: ValueKind::SmallInt,
199    },
200    OpEntry {
201        opcode: 25,
202        arity: Arity::Varargs {
203            min: 0,
204            max: VARARGS_MAX,
205            arg: ValueKind::SmallInt,
206        }, // logior
207        return_kind: ValueKind::SmallInt,
208    },
209    OpEntry {
210        opcode: 26,
211        arity: Arity::Varargs {
212            min: 0,
213            max: VARARGS_MAX,
214            arg: ValueKind::SmallInt,
215        }, // logxor
216        return_kind: ValueKind::SmallInt,
217    },
218    OpEntry {
219        opcode: 27,
220        arity: Arity::Unary(ValueKind::SmallInt), // lognot
221        return_kind: ValueKind::SmallInt,
222    },
223    OpEntry {
224        opcode: 29,
225        arity: Arity::Varargs {
226            min: 0,
227            max: VARARGS_MAX,
228            arg: ValueKind::G1Point,
229        }, // point_add
230        return_kind: ValueKind::G1Point,
231    },
232    OpEntry {
233        opcode: 30,
234        arity: Arity::Unary(ValueKind::SmallInt), // pubkey_for_exp
235        return_kind: ValueKind::G1Point,
236    },
237    OpEntry {
238        opcode: 32,
239        arity: Arity::Unary(ValueKind::Bool), // not
240        return_kind: ValueKind::Bool,
241    },
242    OpEntry {
243        opcode: 33,
244        arity: Arity::Varargs {
245            min: 0,
246            max: VARARGS_MAX,
247            arg: ValueKind::Bool,
248        }, // any
249        return_kind: ValueKind::Bool,
250    },
251    OpEntry {
252        opcode: 34,
253        arity: Arity::Varargs {
254            min: 0,
255            max: VARARGS_MAX,
256            arg: ValueKind::Bool,
257        }, // all
258        return_kind: ValueKind::Bool,
259    },
260    OpEntry {
261        opcode: 36,
262        arity: Arity::Quaternary(
263            ValueKind::SmallInt,
264            ValueKind::SmallInt,
265            ValueKind::Program,
266            ValueKind::Tree,
267        ), // softfork
268        return_kind: ValueKind::Tree,
269    },
270    OpEntry {
271        opcode: 48,
272        arity: Arity::Ternary(ValueKind::Bytes32, ValueKind::Bytes32, ValueKind::SmallInt), // coinid
273        return_kind: ValueKind::Bytes32,
274    },
275    OpEntry {
276        opcode: 49,
277        arity: Arity::Varargs {
278            min: 0,
279            max: VARARGS_MAX,
280            arg: ValueKind::G1Point,
281        }, // bls_g1_subtract
282        return_kind: ValueKind::G1Point,
283    },
284    OpEntry {
285        opcode: 50,
286        arity: Arity::Binary(ValueKind::G1Point, ValueKind::SmallInt), // bls_g1_multiply
287        return_kind: ValueKind::G1Point,
288    },
289    OpEntry {
290        opcode: 51,
291        arity: Arity::Unary(ValueKind::G1Point), // bls_g1_negate
292        return_kind: ValueKind::G1Point,
293    },
294    OpEntry {
295        opcode: 52,
296        arity: Arity::Varargs {
297            min: 0,
298            max: VARARGS_MAX,
299            arg: ValueKind::G2Point,
300        }, // bls_g2_add
301        return_kind: ValueKind::G2Point,
302    },
303    OpEntry {
304        opcode: 53,
305        arity: Arity::Varargs {
306            min: 0,
307            max: VARARGS_MAX,
308            arg: ValueKind::G2Point,
309        }, // bls_g2_subtract
310        return_kind: ValueKind::G2Point,
311    },
312    OpEntry {
313        opcode: 54,
314        arity: Arity::Binary(ValueKind::G2Point, ValueKind::SmallInt), // bls_g2_multiply
315        return_kind: ValueKind::G2Point,
316    },
317    OpEntry {
318        opcode: 55,
319        arity: Arity::Unary(ValueKind::G2Point), // bls_g2_negate
320        return_kind: ValueKind::G2Point,
321    },
322    OpEntry {
323        opcode: 56,
324        arity: Arity::Varargs {
325            min: 1,
326            max: 2,
327            arg: ValueKind::LargeBytes,
328        }, // bls_map_to_g1
329        return_kind: ValueKind::G1Point,
330    },
331    OpEntry {
332        opcode: 57,
333        arity: Arity::Varargs {
334            min: 1,
335            max: 2,
336            arg: ValueKind::LargeBytes,
337        }, // bls_map_to_g2
338        return_kind: ValueKind::G2Point,
339    },
340    OpEntry {
341        opcode: 58,
342        arity: Arity::BlsPairingIdentity {
343            max_pairs: MAX_BLS_PAIRING_PAIRS,
344            g1: ValueKind::G1Point,
345            g2: ValueKind::G2Point,
346        },
347        return_kind: ValueKind::Bool,
348    },
349    OpEntry {
350        opcode: 59,
351        arity: Arity::BlsVerify {
352            max_pairs: MAX_BLS_VERIFY_PAIRS,
353            sig: ValueKind::G2Point,
354            pk: ValueKind::G1Point,
355            msg: ValueKind::LargeBytes,
356        },
357        return_kind: ValueKind::Bool,
358    },
359    OpEntry {
360        opcode: 60,
361        arity: Arity::Ternary(
362            ValueKind::SmallInt,
363            ValueKind::SmallInt,
364            ValueKind::SmallInt,
365        ), // modpow
366        return_kind: ValueKind::SmallInt,
367    },
368    OpEntry {
369        opcode: 61,
370        arity: Arity::Binary(ValueKind::SmallInt, ValueKind::SmallInt), // mod
371        return_kind: ValueKind::SmallInt,
372    },
373    OpEntry {
374        opcode: 62,
375        arity: Arity::Varargs {
376            min: 0,
377            max: VARARGS_MAX,
378            arg: ValueKind::LargeBytes,
379        }, // keccak256
380        return_kind: ValueKind::Bytes32,
381    },
382    OpEntry {
383        opcode: 63,
384        arity: Arity::Unary(ValueKind::Tree), // sha256_tree
385        return_kind: ValueKind::Bytes32,
386    },
387    OpEntry {
388        opcode: 0x13d61f00,
389        arity: Arity::Ternary(
390            ValueKind::LargeBytes,
391            ValueKind::Bytes32,
392            ValueKind::LargeBytes,
393        ),
394        return_kind: ValueKind::Bool,
395    },
396    OpEntry {
397        opcode: 0x1c3a8f00,
398        arity: Arity::Ternary(
399            ValueKind::LargeBytes,
400            ValueKind::Bytes32,
401            ValueKind::LargeBytes,
402        ),
403        return_kind: ValueKind::Bool,
404    },
405];
406
407#[derive(Arbitrary)]
408enum NodeType {
409    Pair,
410    Bytes,
411    U8,
412    U16,
413    U32,
414    Previous,
415}
416
417#[derive(Debug)]
418pub struct ArbitraryClvmTree<const MAX_NODES: i64 = 600_000, const REUSE_NODES: bool = true> {
419    pub allocator: Allocator,
420    pub tree: NodePtr,
421    pub num_nodes: u32,
422}
423
424impl<'a, const MAX_NODES: i64, const REUSE_NODES: bool> Arbitrary<'a>
425    for ArbitraryClvmTree<MAX_NODES, REUSE_NODES>
426{
427    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
428        let mut a = Allocator::new();
429        let (tree, num_nodes) =
430            make_tree_limits(&mut a, u, MAX_NODES, REUSE_NODES).expect("make_tree");
431        Ok(Self {
432            allocator: a,
433            tree,
434            num_nodes,
435        })
436    }
437}
438
439pub fn make_tree(a: &mut Allocator, unstructured: &mut Unstructured<'_>) -> (NodePtr, u32) {
440    make_tree_limits(a, unstructured, 600_000, true).expect("out of memory")
441}
442
443fn arity_args(
444    unstructured: &mut Unstructured<'_>,
445    arity: &Arity,
446) -> anyhow::Result<Vec<ValueKind>> {
447    Ok(match *arity {
448        Arity::Nullary => Vec::new(),
449        Arity::Unary(kind) => vec![kind],
450        Arity::Binary(k0, k1) => vec![k0, k1],
451        Arity::Ternary(k0, k1, k2) => vec![k0, k1, k2],
452        Arity::BinaryOrTernary(k0, k1, k2) => {
453            let argc = unstructured.int_in_range(2..=3)?;
454            if argc == 2 {
455                vec![k0, k1]
456            } else {
457                vec![k0, k1, k2]
458            }
459        }
460        Arity::Quaternary(k0, k1, k2, k3) => vec![k0, k1, k2, k3],
461        Arity::Varargs { min, max, arg } => {
462            let argc = unstructured.int_in_range(min..=max)?;
463            vec![arg; argc]
464        }
465        Arity::BlsVerify {
466            max_pairs,
467            sig,
468            pk,
469            msg,
470        } => {
471            let pairs = unstructured.int_in_range(0..=max_pairs)?;
472            let mut args = Vec::with_capacity(1 + pairs * 2);
473            args.push(sig);
474            for _ in 0..pairs {
475                args.push(pk);
476                args.push(msg);
477            }
478            args
479        }
480        Arity::BlsPairingIdentity { max_pairs, g1, g2 } => {
481            let pairs = unstructured.int_in_range(0..=max_pairs)?;
482            let mut args = Vec::with_capacity(pairs * 2);
483            for _ in 0..pairs {
484                args.push(g1);
485                args.push(g2);
486            }
487            args
488        }
489    })
490}
491
492fn pick_op_by_return_kind(
493    unstructured: &mut Unstructured<'_>,
494    kind: ValueKind,
495) -> anyhow::Result<Option<OpEntry>> {
496    let mut matches = Vec::new();
497    for entry in PROGRAM_OPS {
498        if entry.return_kind == kind {
499            matches.push(*entry);
500        }
501    }
502    if matches.is_empty() {
503        Ok(None)
504    } else {
505        Ok(Some(*unstructured.choose(&matches)?))
506    }
507}
508
509fn pick_env_path(
510    a: &Allocator,
511    unstructured: &mut Unstructured<'_>,
512    env: NodePtr,
513) -> anyhow::Result<u64> {
514    if unstructured.ratio(1, 64)? {
515        return Ok(unstructured.arbitrary::<u64>()?);
516    }
517
518    let mut stack = Vec::new();
519    stack.push((env, 1u64, 0u32));
520    let mut chosen: Option<u64> = None;
521    let mut count: usize = 0;
522
523    while let Some((node, path, depth)) = stack.pop() {
524        count = count.saturating_add(1);
525        if unstructured.int_in_range(1..=count)? == 1 {
526            chosen = Some(path);
527        }
528
529        if depth >= 63 {
530            continue;
531        }
532
533        if let SExp::Pair(left, right) = a.sexp(node) {
534            let base_bits = if depth == 0 {
535                0
536            } else {
537                path & ((1u64 << depth) - 1)
538            };
539            let next_depth = depth + 1;
540            let terminator = 1u64 << next_depth;
541            let left_path = base_bits | terminator;
542            let right_path = base_bits | (1u64 << depth) | terminator;
543            stack.push((left, left_path, next_depth));
544            stack.push((right, right_path, next_depth));
545        }
546    }
547
548    Ok(chosen.unwrap_or_else(|| unstructured.arbitrary::<u64>().unwrap_or(1)))
549}
550
551fn make_list_value(
552    a: &mut Allocator,
553    unstructured: &mut Unstructured<'_>,
554    env: NodePtr,
555    max_nodes: i64,
556) -> anyhow::Result<NodePtr> {
557    let length = unstructured.int_in_range(0..=4)?;
558    let base_nodes = length as i64;
559    let available = (max_nodes - base_nodes).max(0);
560    let per_item_budget = if length == 0 {
561        0
562    } else {
563        (available / length as i64).max(0)
564    };
565
566    let mut list = a.nil();
567    for _ in 0..length {
568        let item = make_value(a, unstructured, ValueKind::Tree, env, env, per_item_budget)?;
569        list = a.new_pair(item, list)?;
570    }
571    Ok(list)
572}
573
574fn make_literal_value(
575    a: &mut Allocator,
576    unstructured: &mut Unstructured<'_>,
577    kind: ValueKind,
578    env: NodePtr,
579    inner_env: NodePtr,
580    max_nodes: i64,
581) -> anyhow::Result<NodePtr> {
582    let value = match kind {
583        ValueKind::Program => make_clvm_program(a, unstructured, inner_env, max_nodes)?,
584        ValueKind::Bytes32 => a.new_atom(unstructured.bytes(32)?)?,
585        ValueKind::G1Point => {
586            if unstructured.ratio(1, 16)? {
587                a.new_atom(unstructured.bytes(48)?)?
588            } else {
589                a.new_g1(G1Element::arbitrary(unstructured)?)?
590            }
591        }
592        ValueKind::G2Point => {
593            if unstructured.ratio(1, 16)? {
594                a.new_atom(unstructured.bytes(96)?)?
595            } else {
596                a.new_g2(G2Element::arbitrary(unstructured)?)?
597            }
598        }
599        ValueKind::LargeBytes => {
600            let len = unstructured.int_in_range(64..=128)?;
601            a.new_atom(unstructured.bytes(len)?)?
602        }
603        ValueKind::EnvPath => {
604            // paths into the environment should not be quoted, so return early
605            // here
606            let val = pick_env_path(a, unstructured, env)?;
607            return Ok(a.new_number(val.into())?);
608        }
609        ValueKind::SmallInt => {
610            let val: u8 = unstructured.arbitrary()?;
611            a.new_number(val.into())?
612        }
613        ValueKind::Bool => *unstructured.choose(&[a.nil(), a.one()])?,
614        ValueKind::List => make_list_value(a, unstructured, env, max_nodes)?,
615        ValueKind::Tree => make_tree_limits(a, unstructured, max_nodes, true)?.0,
616    };
617    let quote = a.one();
618    Ok(a.new_pair(quote, value)?)
619}
620
621fn make_value(
622    a: &mut Allocator,
623    unstructured: &mut Unstructured<'_>,
624    mut kind: ValueKind,
625    env: NodePtr,
626    inner_env: NodePtr,
627    max_nodes: i64,
628) -> anyhow::Result<NodePtr> {
629    // some of the time, we pick a random value type, rather than the expected
630    if unstructured.ratio(1, 32)? {
631        let kind: ValueKind = unstructured.arbitrary()?;
632        return make_literal_value(a, unstructured, kind, env, inner_env, max_nodes);
633    }
634    // Sometimes generate a path into the environment
635    if unstructured.ratio(1, 16)? {
636        kind = ValueKind::EnvPath;
637    }
638
639    // 50% of the time we generate an operator call instead of a literal value
640    if unstructured.ratio(1, 2)?
641        && let Some(entry) = pick_op_by_return_kind(unstructured, kind)?
642    {
643        make_program_with_entry(a, unstructured, entry, env, max_nodes)
644    } else {
645        make_literal_value(a, unstructured, kind, env, inner_env, max_nodes)
646    }
647}
648
649fn make_program_with_entry(
650    a: &mut Allocator,
651    unstructured: &mut Unstructured<'_>,
652    entry: OpEntry,
653    env: NodePtr,
654    max_nodes: i64,
655) -> anyhow::Result<NodePtr> {
656    let args_kinds = arity_args(unstructured, &entry.arity)?;
657    let n = args_kinds.len();
658    let base_nodes = 2 + n;
659    if max_nodes < base_nodes as i64 {
660        return Ok(a.one());
661    }
662    let available = max_nodes - base_nodes as i64;
663    let arg_budget = if n == 0 {
664        0
665    } else {
666        (available / n as i64).max(0)
667    };
668
669    let apply_like = entry.opcode == 2 || entry.opcode == 36;
670
671    let mut args = NodePtr::NIL;
672    for kind in args_kinds.iter().rev() {
673        let inner_env = if apply_like && *kind == ValueKind::Program {
674            // This is a special case for apply and softfork.
675            // When generating the program, we need to use the arguments in
676            // this call as its environment
677            args
678        } else {
679            env
680        };
681        let arg = make_value(a, unstructured, *kind, env, inner_env, arg_budget)?;
682        args = a.new_pair(arg, args)?;
683    }
684
685    let op_atom = a.new_number(entry.opcode.into())?;
686    Ok(a.new_pair(op_atom, args)?)
687}
688
689pub fn make_clvm_program(
690    a: &mut Allocator,
691    unstructured: &mut Unstructured<'_>,
692    env: NodePtr,
693    max_nodes: i64,
694) -> anyhow::Result<NodePtr> {
695    if unstructured.ratio(1, 64)? {
696        return Ok(make_tree_limits(a, unstructured, max_nodes, false)?.0);
697    }
698
699    let entry = *unstructured.choose(PROGRAM_OPS)?;
700    make_program_with_entry(a, unstructured, entry, env, max_nodes)
701}
702
703/// returns an arbitrary CLVM tree structure and the number of (unique) nodes
704/// it's made up of. That's both pairs and atoms.
705pub fn make_tree_limits(
706    a: &mut Allocator,
707    unstructured: &mut Unstructured<'_>,
708    mut max_nodes: i64,
709    reuse_nodes: bool,
710) -> anyhow::Result<(NodePtr, u32)> {
711    let mut previous_nodes = Vec::new();
712    let mut value_stack = Vec::new();
713    let mut op_stack = vec![Op::SubTree];
714    // the number of Op::SubTree items on the op_stack
715    let mut sub_trees: i64 = 1;
716    let mut counter = 0;
717
718    while let Some(op) = op_stack.pop() {
719        match op {
720            Op::Pair(swap) => {
721                let left = value_stack.pop().expect("internal error, empty stack");
722                let right = value_stack.pop().expect("internal error, empty stack");
723                let pair = if swap {
724                    a.new_pair(left, right)?
725                } else {
726                    a.new_pair(right, left)?
727                };
728                counter += 1;
729                value_stack.push(pair);
730                previous_nodes.push(pair);
731            }
732            Op::SubTree => {
733                sub_trees -= 1;
734                if unstructured.is_empty() {
735                    value_stack.push(NodePtr::NIL);
736                    continue;
737                }
738                match unstructured.arbitrary::<NodeType>() {
739                    Err(..) => value_stack.push(NodePtr::NIL),
740                    Ok(NodeType::Pair) => {
741                        if sub_trees > unstructured.len() as i64 || max_nodes <= 0 {
742                            // there isn't much entropy left, don't grow the
743                            // tree anymore
744                            value_stack.push(if reuse_nodes {
745                                *unstructured
746                                    .choose(&previous_nodes)
747                                    .unwrap_or(&NodePtr::NIL)
748                            } else {
749                                NodePtr::NIL
750                            });
751                        } else {
752                            // swap left and right arbitrarily, to avoid
753                            // having a bias because we build the tree depth
754                            // first, until we run out of entropy
755                            let swap = unstructured.arbitrary::<bool>() == Ok(true);
756                            op_stack.push(Op::Pair(swap));
757                            op_stack.push(Op::SubTree);
758                            op_stack.push(Op::SubTree);
759                            sub_trees += 2;
760                            max_nodes -= 2;
761                        }
762                    }
763                    Ok(NodeType::Bytes) => {
764                        counter += 1;
765                        value_stack.push(match unstructured.arbitrary::<Vec<u8>>() {
766                            Err(..) => NodePtr::NIL,
767                            Ok(val) => {
768                                let node = a.new_atom(&val)?;
769                                previous_nodes.push(node);
770                                node
771                            }
772                        });
773                    }
774                    Ok(NodeType::U8) => {
775                        counter += 1;
776                        value_stack.push(match unstructured.arbitrary::<u8>() {
777                            Err(..) => NodePtr::NIL,
778                            Ok(val) => a.new_small_number(val.into())?,
779                        });
780                    }
781                    Ok(NodeType::U16) => {
782                        counter += 1;
783                        value_stack.push(match unstructured.arbitrary::<u16>() {
784                            Err(..) => NodePtr::NIL,
785                            Ok(val) => a.new_small_number(val.into())?,
786                        });
787                    }
788                    Ok(NodeType::U32) => {
789                        counter += 1;
790                        value_stack.push(match unstructured.arbitrary::<u32>() {
791                            Err(..) => NodePtr::NIL,
792                            Ok(val) => a.new_number(val.into())?,
793                        });
794                    }
795                    Ok(NodeType::Previous) => {
796                        value_stack.push(if reuse_nodes {
797                            *unstructured
798                                .choose(&previous_nodes)
799                                .unwrap_or(&NodePtr::NIL)
800                        } else {
801                            NodePtr::NIL
802                        });
803                    }
804                }
805            }
806        }
807    }
808    assert_eq!(value_stack.len(), 1);
809    Ok((value_stack.remove(0), counter))
810}
811
812pub fn make_list(a: &mut Allocator, unstructured: &mut Unstructured<'_>) -> NodePtr {
813    let mut ret = NodePtr::NIL;
814
815    let length = unstructured.arbitrary::<u8>().unwrap_or(0);
816    let nil_terminated = unstructured.arbitrary::<bool>().unwrap_or(false);
817
818    for _ in 0..length {
819        let value = match unstructured
820            .arbitrary::<NodeType>()
821            .unwrap_or(NodeType::Previous)
822        {
823            NodeType::U8 => a
824                .new_small_number(unstructured.arbitrary::<u8>().unwrap_or(0).into())
825                .unwrap(),
826
827            NodeType::U16 => a
828                .new_small_number(unstructured.arbitrary::<u16>().unwrap_or(0).into())
829                .unwrap(),
830
831            NodeType::U32 => a
832                .new_number(unstructured.arbitrary::<u32>().unwrap_or(0).into())
833                .unwrap(),
834
835            NodeType::Bytes => a
836                .new_atom(&unstructured.arbitrary::<Vec<u8>>().unwrap_or_default())
837                .unwrap(),
838
839            NodeType::Pair => {
840                let left = NodePtr::NIL;
841                let right = NodePtr::NIL;
842                a.new_pair(left, right).unwrap()
843            }
844
845            NodeType::Previous => NodePtr::NIL,
846        };
847
848        ret = if nil_terminated {
849            a.new_pair(value, ret).unwrap()
850        } else {
851            value
852        };
853    }
854
855    ret
856}