From e5eef98a6ea26efdaf3654553785065007ec5a41 Mon Sep 17 00:00:00 2001 From: Rigidity <35380458+Rigidity@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:55:56 -0400 Subject: [PATCH] Test modules (#464) * Allocator test module * More transitioning to modules * Final op_utils tetss * run_program * test_ops * traverse_path * Remainder --- src/allocator.rs | 2119 ++++++++++++++++---------------- src/more_ops.rs | 70 +- src/number.rs | 443 +++---- src/op_utils.rs | 863 +++++++------ src/run_program.rs | 1816 ++++++++++++++------------- src/serde/de_br.rs | 176 +-- src/serde/de_tree.rs | 242 ++-- src/serde/object_cache.rs | 215 ++-- src/serde/parse_atom.rs | 234 ++-- src/serde/read_cache_lookup.rs | 324 ++--- src/serde/ser.rs | 61 +- src/serde/ser_br.rs | 33 +- src/serde/tools.rs | 402 +++--- src/serde/write_atom.rs | 221 ++-- src/test_ops.rs | 597 ++++----- src/tests.rs | 2 - src/traverse_path.rs | 301 ++--- 17 files changed, 4051 insertions(+), 4068 deletions(-) diff --git a/src/allocator.rs b/src/allocator.rs index 7b258388..7e7f5bbe 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -2,6 +2,7 @@ use crate::err_utils::err; use crate::number::{number_from_u8, Number}; use crate::reduction::EvalErr; use chia_bls::{G1Element, G2Element}; +use std::borrow::Borrow; use std::hash::Hash; use std::hash::Hasher; use std::ops::Deref; @@ -667,824 +668,798 @@ impl Allocator { } } -#[test] -fn test_atom_eq_1() { - // these are a bunch of different representations of 1 - // make sure they all compare equal - let mut a = Allocator::new(); - let a0 = a.one(); - let a1 = a.new_atom(&[1]).unwrap(); - let a2 = { - let tmp = a.new_atom(&[0x01, 0xff]).unwrap(); - a.new_substr(tmp, 0, 1).unwrap() - }; - let a3 = a.new_substr(a2, 0, 1).unwrap(); - let a4 = a.new_number(1.into()).unwrap(); - let a5 = a.new_small_number(1).unwrap(); - - assert!(a.atom_eq(a0, a0)); - assert!(a.atom_eq(a0, a1)); - assert!(a.atom_eq(a0, a2)); - assert!(a.atom_eq(a0, a3)); - assert!(a.atom_eq(a0, a4)); - assert!(a.atom_eq(a0, a5)); - - assert!(a.atom_eq(a1, a0)); - assert!(a.atom_eq(a1, a1)); - assert!(a.atom_eq(a1, a2)); - assert!(a.atom_eq(a1, a3)); - assert!(a.atom_eq(a1, a4)); - assert!(a.atom_eq(a1, a5)); - - assert!(a.atom_eq(a2, a0)); - assert!(a.atom_eq(a2, a1)); - assert!(a.atom_eq(a2, a2)); - assert!(a.atom_eq(a2, a3)); - assert!(a.atom_eq(a2, a4)); - assert!(a.atom_eq(a2, a5)); - - assert!(a.atom_eq(a3, a0)); - assert!(a.atom_eq(a3, a1)); - assert!(a.atom_eq(a3, a2)); - assert!(a.atom_eq(a3, a3)); - assert!(a.atom_eq(a3, a4)); - assert!(a.atom_eq(a3, a5)); - - assert!(a.atom_eq(a4, a0)); - assert!(a.atom_eq(a4, a1)); - assert!(a.atom_eq(a4, a2)); - assert!(a.atom_eq(a4, a3)); - assert!(a.atom_eq(a4, a4)); - assert!(a.atom_eq(a4, a5)); - - assert!(a.atom_eq(a5, a0)); - assert!(a.atom_eq(a5, a1)); - assert!(a.atom_eq(a5, a2)); - assert!(a.atom_eq(a5, a3)); - assert!(a.atom_eq(a5, a4)); - assert!(a.atom_eq(a5, a5)); -} - -#[test] -fn test_atom_eq_minus_1() { - // these are a bunch of different representations of -1 - // make sure they all compare equal - let mut a = Allocator::new(); - let a0 = a.new_atom(&[0xff]).unwrap(); - let a1 = a.new_number((-1).into()).unwrap(); - let a2 = { - let tmp = a.new_atom(&[0x01, 0xff]).unwrap(); - a.new_substr(tmp, 1, 2).unwrap() - }; - let a3 = a.new_substr(a0, 0, 1).unwrap(); - - assert!(a.atom_eq(a0, a0)); - assert!(a.atom_eq(a0, a1)); - assert!(a.atom_eq(a0, a2)); - assert!(a.atom_eq(a0, a3)); - - assert!(a.atom_eq(a1, a0)); - assert!(a.atom_eq(a1, a1)); - assert!(a.atom_eq(a1, a2)); - assert!(a.atom_eq(a1, a3)); - - assert!(a.atom_eq(a2, a0)); - assert!(a.atom_eq(a2, a1)); - assert!(a.atom_eq(a2, a2)); - assert!(a.atom_eq(a2, a3)); - - assert!(a.atom_eq(a3, a0)); - assert!(a.atom_eq(a3, a1)); - assert!(a.atom_eq(a3, a2)); - assert!(a.atom_eq(a3, a3)); -} - -#[test] -fn test_atom_eq() { - let mut a = Allocator::new(); - let a0 = a.nil(); - let a1 = a.one(); - let a2 = a.new_atom(&[1]).unwrap(); - let a3 = a.new_atom(&[0xfa, 0xc7]).unwrap(); - let a4 = a.new_small_number(1).unwrap(); - let a5 = a.new_number((-1337).into()).unwrap(); - - assert!(a.atom_eq(a0, a0)); - assert!(!a.atom_eq(a0, a1)); - assert!(!a.atom_eq(a0, a2)); - assert!(!a.atom_eq(a0, a3)); - assert!(!a.atom_eq(a0, a4)); - assert!(!a.atom_eq(a0, a5)); - - assert!(!a.atom_eq(a1, a0)); - assert!(a.atom_eq(a1, a1)); - assert!(a.atom_eq(a1, a2)); - assert!(!a.atom_eq(a1, a3)); - assert!(a.atom_eq(a1, a4)); - assert!(!a.atom_eq(a1, a5)); - - assert!(!a.atom_eq(a2, a0)); - assert!(a.atom_eq(a2, a1)); - assert!(a.atom_eq(a2, a2)); - assert!(!a.atom_eq(a2, a3)); - assert!(a.atom_eq(a2, a4)); - assert!(!a.atom_eq(a2, a5)); - - assert!(!a.atom_eq(a3, a0)); - assert!(!a.atom_eq(a3, a1)); - assert!(!a.atom_eq(a3, a2)); - assert!(a.atom_eq(a3, a3)); - assert!(!a.atom_eq(a3, a4)); - assert!(a.atom_eq(a3, a5)); - - assert!(!a.atom_eq(a4, a0)); - assert!(a.atom_eq(a4, a1)); - assert!(a.atom_eq(a4, a2)); - assert!(!a.atom_eq(a4, a3)); - assert!(a.atom_eq(a4, a4)); - assert!(!a.atom_eq(a4, a5)); -} +#[cfg(test)] +mod tests { + use rstest::rstest; + + use super::*; + + #[test] + fn test_atom_eq_1() { + // these are a bunch of different representations of 1 + // make sure they all compare equal + let mut a = Allocator::new(); + let a0 = a.one(); + let a1 = a.new_atom(&[1]).unwrap(); + let a2 = { + let tmp = a.new_atom(&[0x01, 0xff]).unwrap(); + a.new_substr(tmp, 0, 1).unwrap() + }; + let a3 = a.new_substr(a2, 0, 1).unwrap(); + let a4 = a.new_number(1.into()).unwrap(); + let a5 = a.new_small_number(1).unwrap(); + + assert!(a.atom_eq(a0, a0)); + assert!(a.atom_eq(a0, a1)); + assert!(a.atom_eq(a0, a2)); + assert!(a.atom_eq(a0, a3)); + assert!(a.atom_eq(a0, a4)); + assert!(a.atom_eq(a0, a5)); + + assert!(a.atom_eq(a1, a0)); + assert!(a.atom_eq(a1, a1)); + assert!(a.atom_eq(a1, a2)); + assert!(a.atom_eq(a1, a3)); + assert!(a.atom_eq(a1, a4)); + assert!(a.atom_eq(a1, a5)); + + assert!(a.atom_eq(a2, a0)); + assert!(a.atom_eq(a2, a1)); + assert!(a.atom_eq(a2, a2)); + assert!(a.atom_eq(a2, a3)); + assert!(a.atom_eq(a2, a4)); + assert!(a.atom_eq(a2, a5)); + + assert!(a.atom_eq(a3, a0)); + assert!(a.atom_eq(a3, a1)); + assert!(a.atom_eq(a3, a2)); + assert!(a.atom_eq(a3, a3)); + assert!(a.atom_eq(a3, a4)); + assert!(a.atom_eq(a3, a5)); + + assert!(a.atom_eq(a4, a0)); + assert!(a.atom_eq(a4, a1)); + assert!(a.atom_eq(a4, a2)); + assert!(a.atom_eq(a4, a3)); + assert!(a.atom_eq(a4, a4)); + assert!(a.atom_eq(a4, a5)); + + assert!(a.atom_eq(a5, a0)); + assert!(a.atom_eq(a5, a1)); + assert!(a.atom_eq(a5, a2)); + assert!(a.atom_eq(a5, a3)); + assert!(a.atom_eq(a5, a4)); + assert!(a.atom_eq(a5, a5)); + } -#[test] -#[should_panic] -fn test_atom_eq_pair1() { - let mut a = Allocator::new(); - let a0 = a.nil(); - let pair = a.new_pair(a0, a0).unwrap(); - a.atom_eq(pair, a0); -} + #[test] + fn test_atom_eq_minus_1() { + // these are a bunch of different representations of -1 + // make sure they all compare equal + let mut a = Allocator::new(); + let a0 = a.new_atom(&[0xff]).unwrap(); + let a1 = a.new_number((-1).into()).unwrap(); + let a2 = { + let tmp = a.new_atom(&[0x01, 0xff]).unwrap(); + a.new_substr(tmp, 1, 2).unwrap() + }; + let a3 = a.new_substr(a0, 0, 1).unwrap(); + + assert!(a.atom_eq(a0, a0)); + assert!(a.atom_eq(a0, a1)); + assert!(a.atom_eq(a0, a2)); + assert!(a.atom_eq(a0, a3)); + + assert!(a.atom_eq(a1, a0)); + assert!(a.atom_eq(a1, a1)); + assert!(a.atom_eq(a1, a2)); + assert!(a.atom_eq(a1, a3)); + + assert!(a.atom_eq(a2, a0)); + assert!(a.atom_eq(a2, a1)); + assert!(a.atom_eq(a2, a2)); + assert!(a.atom_eq(a2, a3)); + + assert!(a.atom_eq(a3, a0)); + assert!(a.atom_eq(a3, a1)); + assert!(a.atom_eq(a3, a2)); + assert!(a.atom_eq(a3, a3)); + } -#[test] -#[should_panic] -fn test_atom_eq_pair2() { - let mut a = Allocator::new(); - let a0 = a.nil(); - let pair = a.new_pair(a0, a0).unwrap(); - a.atom_eq(a0, pair); -} + #[test] + fn test_atom_eq() { + let mut a = Allocator::new(); + let a0 = a.nil(); + let a1 = a.one(); + let a2 = a.new_atom(&[1]).unwrap(); + let a3 = a.new_atom(&[0xfa, 0xc7]).unwrap(); + let a4 = a.new_small_number(1).unwrap(); + let a5 = a.new_number((-1337).into()).unwrap(); + + assert!(a.atom_eq(a0, a0)); + assert!(!a.atom_eq(a0, a1)); + assert!(!a.atom_eq(a0, a2)); + assert!(!a.atom_eq(a0, a3)); + assert!(!a.atom_eq(a0, a4)); + assert!(!a.atom_eq(a0, a5)); + + assert!(!a.atom_eq(a1, a0)); + assert!(a.atom_eq(a1, a1)); + assert!(a.atom_eq(a1, a2)); + assert!(!a.atom_eq(a1, a3)); + assert!(a.atom_eq(a1, a4)); + assert!(!a.atom_eq(a1, a5)); + + assert!(!a.atom_eq(a2, a0)); + assert!(a.atom_eq(a2, a1)); + assert!(a.atom_eq(a2, a2)); + assert!(!a.atom_eq(a2, a3)); + assert!(a.atom_eq(a2, a4)); + assert!(!a.atom_eq(a2, a5)); + + assert!(!a.atom_eq(a3, a0)); + assert!(!a.atom_eq(a3, a1)); + assert!(!a.atom_eq(a3, a2)); + assert!(a.atom_eq(a3, a3)); + assert!(!a.atom_eq(a3, a4)); + assert!(a.atom_eq(a3, a5)); + + assert!(!a.atom_eq(a4, a0)); + assert!(a.atom_eq(a4, a1)); + assert!(a.atom_eq(a4, a2)); + assert!(!a.atom_eq(a4, a3)); + assert!(a.atom_eq(a4, a4)); + assert!(!a.atom_eq(a4, a5)); + } -#[test] -#[should_panic] -fn test_atom_len_pair() { - let mut a = Allocator::new(); - let a0 = a.nil(); - let pair = a.new_pair(a0, a0).unwrap(); - a.atom_len(pair); -} + #[test] + #[should_panic] + fn test_atom_eq_pair1() { + let mut a = Allocator::new(); + let a0 = a.nil(); + let pair = a.new_pair(a0, a0).unwrap(); + a.atom_eq(pair, a0); + } -#[test] -#[should_panic] -fn test_number_pair() { - let mut a = Allocator::new(); - let a0 = a.nil(); - let pair = a.new_pair(a0, a0).unwrap(); - a.number(pair); -} + #[test] + #[should_panic] + fn test_atom_eq_pair2() { + let mut a = Allocator::new(); + let a0 = a.nil(); + let pair = a.new_pair(a0, a0).unwrap(); + a.atom_eq(a0, pair); + } -#[test] -#[should_panic] -fn test_invalid_node_ptr_type() { - let node = NodePtr(3 << NODE_PTR_IDX_BITS); - // unknown NodePtr type - let _ = node.object_type(); -} + #[test] + #[should_panic] + fn test_atom_len_pair() { + let mut a = Allocator::new(); + let a0 = a.nil(); + let pair = a.new_pair(a0, a0).unwrap(); + a.atom_len(pair); + } -#[cfg(debug_assertions)] -#[test] -#[should_panic] -fn test_node_ptr_overflow() { - NodePtr::new(ObjectType::Bytes, NODE_PTR_IDX_MASK as usize + 1); -} + #[test] + #[should_panic] + fn test_number_pair() { + let mut a = Allocator::new(); + let a0 = a.nil(); + let pair = a.new_pair(a0, a0).unwrap(); + a.number(pair); + } -#[cfg(debug_assertions)] -#[test] -#[should_panic] -fn test_invalid_small_number() { - let mut a = Allocator::new(); - a.new_small_number(NODE_PTR_IDX_MASK + 1).unwrap(); -} + #[test] + #[should_panic] + fn test_invalid_node_ptr_type() { + let node = NodePtr(3 << NODE_PTR_IDX_BITS); + // unknown NodePtr type + let _ = node.object_type(); + } -#[cfg(test)] -#[rstest] -#[case(0, 0)] -#[case(1, 1)] -#[case(0x7f, 1)] -#[case(0x80, 2)] -#[case(0x7fff, 2)] -#[case(0x7fffff, 3)] -#[case(0x800000, 4)] -#[case(0x7fffffff, 4)] -#[case(0x80000000, 5)] -#[case(0xffffffff, 5)] -fn test_len_for_value(#[case] val: u32, #[case] len: usize) { - assert_eq!(len_for_value(val), len); -} + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn test_node_ptr_overflow() { + NodePtr::new(ObjectType::Bytes, NODE_PTR_IDX_MASK as usize + 1); + } -#[test] -fn test_nil() { - let a = Allocator::new(); - assert_eq!(a.atom(a.nil()).as_ref(), b""); - assert_eq!(a.sexp(a.nil()), SExp::Atom); - assert_eq!(a.nil(), NodePtr::default()); - assert_eq!(a.nil(), NodePtr::NIL); -} + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn test_invalid_small_number() { + let mut a = Allocator::new(); + a.new_small_number(NODE_PTR_IDX_MASK + 1).unwrap(); + } -#[test] -fn test_one() { - let a = Allocator::new(); - assert_eq!(a.atom(a.one()).as_ref(), b"\x01"); - assert_eq!(a.sexp(a.one()), SExp::Atom); -} + #[rstest] + #[case(0, 0)] + #[case(1, 1)] + #[case(0x7f, 1)] + #[case(0x80, 2)] + #[case(0x7fff, 2)] + #[case(0x7fffff, 3)] + #[case(0x800000, 4)] + #[case(0x7fffffff, 4)] + #[case(0x80000000, 5)] + #[case(0xffffffff, 5)] + fn test_len_for_value(#[case] val: u32, #[case] len: usize) { + assert_eq!(len_for_value(val), len); + } -#[test] -fn test_allocate_atom() { - let mut a = Allocator::new(); - let atom = a.new_atom(b"foobar").unwrap(); - assert_eq!(a.atom(atom).as_ref(), b"foobar"); - assert_eq!(a.sexp(atom), SExp::Atom); -} + #[test] + fn test_nil() { + let a = Allocator::new(); + assert_eq!(a.atom(a.nil()).as_ref(), b""); + assert_eq!(a.sexp(a.nil()), SExp::Atom); + assert_eq!(a.nil(), NodePtr::default()); + assert_eq!(a.nil(), NodePtr::NIL); + } -#[test] -fn test_allocate_pair() { - let mut a = Allocator::new(); - let atom1 = a.new_atom(b"foo").unwrap(); - let atom2 = a.new_atom(b"bar").unwrap(); - let pair = a.new_pair(atom1, atom2).unwrap(); + #[test] + fn test_one() { + let a = Allocator::new(); + assert_eq!(a.atom(a.one()).as_ref(), b"\x01"); + assert_eq!(a.sexp(a.one()), SExp::Atom); + } - assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2)); + #[test] + fn test_allocate_atom() { + let mut a = Allocator::new(); + let atom = a.new_atom(b"foobar").unwrap(); + assert_eq!(a.atom(atom).as_ref(), b"foobar"); + assert_eq!(a.sexp(atom), SExp::Atom); + } - let pair2 = a.new_pair(pair, pair).unwrap(); - assert_eq!(a.sexp(pair2), SExp::Pair(pair, pair)); -} + #[test] + fn test_allocate_pair() { + let mut a = Allocator::new(); + let atom1 = a.new_atom(b"foo").unwrap(); + let atom2 = a.new_atom(b"bar").unwrap(); + let pair = a.new_pair(atom1, atom2).unwrap(); -#[test] -fn test_allocate_heap_limit() { - let mut a = Allocator::new_limited(6); - // we can't allocate 6 bytes - assert_eq!(a.new_atom(b"foobar").unwrap_err().1, "out of memory"); - // but 5 is OK - let _atom = a.new_atom(b"fooba").unwrap(); -} + assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2)); -#[test] -fn test_allocate_atom_limit() { - let mut a = Allocator::new(); + let pair2 = a.new_pair(pair, pair).unwrap(); + assert_eq!(a.sexp(pair2), SExp::Pair(pair, pair)); + } - for _ in 0..MAX_NUM_ATOMS - 2 { - // exhaust the number of atoms allowed to be allocated - let _ = a.new_atom(b"foo").unwrap(); + #[test] + fn test_allocate_heap_limit() { + let mut a = Allocator::new_limited(6); + // we can't allocate 6 bytes + assert_eq!(a.new_atom(b"foobar").unwrap_err().1, "out of memory"); + // but 5 is OK + let _atom = a.new_atom(b"fooba").unwrap(); } - assert_eq!(a.new_atom(b"foobar").unwrap_err().1, "too many atoms"); - assert_eq!(a.u8_vec.len(), 0); - assert_eq!(a.small_atoms, MAX_NUM_ATOMS); -} -#[test] -fn test_allocate_small_number_limit() { - let mut a = Allocator::new(); + #[test] + fn test_allocate_atom_limit() { + let mut a = Allocator::new(); - for _ in 0..MAX_NUM_ATOMS - 2 { - // exhaust the number of atoms allowed to be allocated - let _ = a.new_atom(b"foo").unwrap(); + for _ in 0..MAX_NUM_ATOMS - 2 { + // exhaust the number of atoms allowed to be allocated + let _ = a.new_atom(b"foo").unwrap(); + } + assert_eq!(a.new_atom(b"foobar").unwrap_err().1, "too many atoms"); + assert_eq!(a.u8_vec.len(), 0); + assert_eq!(a.small_atoms, MAX_NUM_ATOMS); } - assert_eq!(a.new_small_number(3).unwrap_err().1, "too many atoms"); - assert_eq!(a.u8_vec.len(), 0); - assert_eq!(a.small_atoms, MAX_NUM_ATOMS); -} -#[test] -fn test_allocate_substr_limit() { - let mut a = Allocator::new(); + #[test] + fn test_allocate_small_number_limit() { + let mut a = Allocator::new(); - for _ in 0..MAX_NUM_ATOMS - 3 { - // exhaust the number of atoms allowed to be allocated - let _ = a.new_atom(b"foo").unwrap(); + for _ in 0..MAX_NUM_ATOMS - 2 { + // exhaust the number of atoms allowed to be allocated + let _ = a.new_atom(b"foo").unwrap(); + } + assert_eq!(a.new_small_number(3).unwrap_err().1, "too many atoms"); + assert_eq!(a.u8_vec.len(), 0); + assert_eq!(a.small_atoms, MAX_NUM_ATOMS); } - let atom = a.new_atom(b"foo").unwrap(); - assert_eq!(a.new_substr(atom, 1, 2).unwrap_err().1, "too many atoms"); - assert_eq!(a.u8_vec.len(), 0); - assert_eq!(a.small_atoms, MAX_NUM_ATOMS); -} -#[test] -fn test_allocate_concat_limit() { - let mut a = Allocator::new(); + #[test] + fn test_allocate_substr_limit() { + let mut a = Allocator::new(); - for _ in 0..MAX_NUM_ATOMS - 3 { - // exhaust the number of atoms allowed to be allocated - let _ = a.new_atom(b"foo").unwrap(); + for _ in 0..MAX_NUM_ATOMS - 3 { + // exhaust the number of atoms allowed to be allocated + let _ = a.new_atom(b"foo").unwrap(); + } + let atom = a.new_atom(b"foo").unwrap(); + assert_eq!(a.new_substr(atom, 1, 2).unwrap_err().1, "too many atoms"); + assert_eq!(a.u8_vec.len(), 0); + assert_eq!(a.small_atoms, MAX_NUM_ATOMS); } - let atom = a.new_atom(b"foo").unwrap(); - assert_eq!(a.new_concat(3, &[atom]).unwrap_err().1, "too many atoms"); - assert_eq!(a.u8_vec.len(), 0); - assert_eq!(a.small_atoms, MAX_NUM_ATOMS); -} -#[test] -fn test_allocate_pair_limit() { - let mut a = Allocator::new(); - let atom = a.new_atom(b"foo").unwrap(); - // one pair is OK - let _pair1 = a.new_pair(atom, atom).unwrap(); - for _ in 1..MAX_NUM_PAIRS { - // exhaust the number of pairs allowed to be allocated - let _ = a.new_pair(atom, atom).unwrap(); - } + #[test] + fn test_allocate_concat_limit() { + let mut a = Allocator::new(); - assert_eq!(a.new_pair(atom, atom).unwrap_err().1, "too many pairs"); -} + for _ in 0..MAX_NUM_ATOMS - 3 { + // exhaust the number of atoms allowed to be allocated + let _ = a.new_atom(b"foo").unwrap(); + } + let atom = a.new_atom(b"foo").unwrap(); + assert_eq!(a.new_concat(3, &[atom]).unwrap_err().1, "too many atoms"); + assert_eq!(a.u8_vec.len(), 0); + assert_eq!(a.small_atoms, MAX_NUM_ATOMS); + } -#[test] -fn test_substr() { - let mut a = Allocator::new(); - let atom = a.new_atom(b"foobar").unwrap(); - let pair = a.new_pair(atom, atom).unwrap(); - - let sub = a.new_substr(atom, 0, 1).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b"f"); - let sub = a.new_substr(atom, 1, 6).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b"oobar"); - let sub = a.new_substr(atom, 1, 1).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b""); - let sub = a.new_substr(atom, 0, 0).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b""); - - assert_eq!( - a.new_substr(atom, 1, 0).unwrap_err().1, - "substr invalid bounds" - ); - assert_eq!( - a.new_substr(atom, 7, 7).unwrap_err().1, - "substr start out of bounds" - ); - assert_eq!( - a.new_substr(atom, 0, 7).unwrap_err().1, - "substr end out of bounds" - ); - assert_eq!( - a.new_substr(atom, u32::MAX, 4).unwrap_err().1, - "substr start out of bounds" - ); - assert_eq!( - a.new_substr(pair, 0, 0).unwrap_err().1, - "(internal error) substr expected atom, got pair" - ); -} + #[test] + fn test_allocate_pair_limit() { + let mut a = Allocator::new(); + let atom = a.new_atom(b"foo").unwrap(); + // one pair is OK + let _pair1 = a.new_pair(atom, atom).unwrap(); + for _ in 1..MAX_NUM_PAIRS { + // exhaust the number of pairs allowed to be allocated + let _ = a.new_pair(atom, atom).unwrap(); + } -#[test] -fn test_substr_small_number() { - let mut a = Allocator::new(); - let atom = a.new_atom(b"a\x80").unwrap(); - assert!(a.small_number(atom).is_some()); - - let sub = a.new_substr(atom, 0, 1).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b"a"); - assert!(a.small_number(sub).is_some()); - let sub = a.new_substr(atom, 1, 2).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b"\x80"); - assert!(a.small_number(sub).is_none()); - let sub = a.new_substr(atom, 1, 1).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b""); - let sub = a.new_substr(atom, 0, 0).unwrap(); - assert_eq!(a.atom(sub).as_ref(), b""); - - assert_eq!( - a.new_substr(atom, 1, 0).unwrap_err().1, - "substr invalid bounds" - ); - assert_eq!( - a.new_substr(atom, 3, 3).unwrap_err().1, - "substr start out of bounds" - ); - assert_eq!( - a.new_substr(atom, 0, 3).unwrap_err().1, - "substr end out of bounds" - ); - assert_eq!( - a.new_substr(atom, u32::MAX, 2).unwrap_err().1, - "substr start out of bounds" - ); -} + assert_eq!(a.new_pair(atom, atom).unwrap_err().1, "too many pairs"); + } -#[test] -fn test_concat_launder_small_number() { - let mut a = Allocator::new(); - let atom1 = a.new_small_number(42).expect("new_small_number"); - assert_eq!(a.small_number(atom1), Some(42)); - - // this "launders" the small number into actually being allocated on the - // heap - let atom2 = a - .new_concat(1, &[a.nil(), atom1, a.nil()]) - .expect("new_substr"); - - // even though this atom is allocated on the heap (and not stored as a small - // int), we can still retrieve it as one. The CLVM interpreter depends on - // this when matching operators against quote, apply and softfork. - assert_eq!(a.small_number(atom2), Some(42)); - assert_eq!(a.atom_len(atom2), 1); - assert_eq!(a.atom(atom2).as_ref(), &[42]); -} + #[test] + fn test_substr() { + let mut a = Allocator::new(); + let atom = a.new_atom(b"foobar").unwrap(); + let pair = a.new_pair(atom, atom).unwrap(); + + let sub = a.new_substr(atom, 0, 1).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b"f"); + let sub = a.new_substr(atom, 1, 6).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b"oobar"); + let sub = a.new_substr(atom, 1, 1).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b""); + let sub = a.new_substr(atom, 0, 0).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b""); + + assert_eq!( + a.new_substr(atom, 1, 0).unwrap_err().1, + "substr invalid bounds" + ); + assert_eq!( + a.new_substr(atom, 7, 7).unwrap_err().1, + "substr start out of bounds" + ); + assert_eq!( + a.new_substr(atom, 0, 7).unwrap_err().1, + "substr end out of bounds" + ); + assert_eq!( + a.new_substr(atom, u32::MAX, 4).unwrap_err().1, + "substr start out of bounds" + ); + assert_eq!( + a.new_substr(pair, 0, 0).unwrap_err().1, + "(internal error) substr expected atom, got pair" + ); + } -#[test] -fn test_concat() { - let mut a = Allocator::new(); - let atom1 = a.new_atom(b"f").unwrap(); - let atom2 = a.new_atom(b"o").unwrap(); - let atom3 = a.new_atom(b"o").unwrap(); - let atom4 = a.new_atom(b"b").unwrap(); - let atom5 = a.new_atom(b"a").unwrap(); - let atom6 = a.new_atom(b"r").unwrap(); - let pair = a.new_pair(atom1, atom2).unwrap(); - - let cat = a - .new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6]) - .unwrap(); - assert_eq!(a.atom(cat).as_ref(), b"foobar"); - - let cat = a.new_concat(12, &[cat, cat]).unwrap(); - assert_eq!(a.atom(cat).as_ref(), b"foobarfoobar"); - - assert_eq!( - a.new_concat(11, &[cat, cat]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); - assert_eq!( - a.new_concat(13, &[cat, cat]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); - assert_eq!( - a.new_concat(12, &[atom3, pair]).unwrap_err().1, - "(internal error) concat expected atom, got pair" - ); - - assert_eq!( - a.new_concat(4, &[atom1, atom2, atom3]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); - - assert_eq!( - a.new_concat(2, &[atom1, atom2, atom3]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); -} + #[test] + fn test_substr_small_number() { + let mut a = Allocator::new(); + let atom = a.new_atom(b"a\x80").unwrap(); + assert!(a.small_number(atom).is_some()); + + let sub = a.new_substr(atom, 0, 1).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b"a"); + assert!(a.small_number(sub).is_some()); + let sub = a.new_substr(atom, 1, 2).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b"\x80"); + assert!(a.small_number(sub).is_none()); + let sub = a.new_substr(atom, 1, 1).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b""); + let sub = a.new_substr(atom, 0, 0).unwrap(); + assert_eq!(a.atom(sub).as_ref(), b""); + + assert_eq!( + a.new_substr(atom, 1, 0).unwrap_err().1, + "substr invalid bounds" + ); + assert_eq!( + a.new_substr(atom, 3, 3).unwrap_err().1, + "substr start out of bounds" + ); + assert_eq!( + a.new_substr(atom, 0, 3).unwrap_err().1, + "substr end out of bounds" + ); + assert_eq!( + a.new_substr(atom, u32::MAX, 2).unwrap_err().1, + "substr start out of bounds" + ); + } -#[test] -fn test_concat_large() { - let mut a = Allocator::new(); - let atom1 = a.new_atom(b"foo").unwrap(); - let atom2 = a.new_atom(b"bar").unwrap(); - let pair = a.new_pair(atom1, atom2).unwrap(); - - let cat = a.new_concat(6, &[atom1, atom2]).unwrap(); - assert_eq!(a.atom(cat).as_ref(), b"foobar"); - - let cat = a.new_concat(12, &[cat, cat]).unwrap(); - assert_eq!(a.atom(cat).as_ref(), b"foobarfoobar"); - - assert_eq!( - a.new_concat(11, &[cat, cat]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); - assert_eq!( - a.new_concat(13, &[cat, cat]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); - assert_eq!( - a.new_concat(12, &[atom1, pair]).unwrap_err().1, - "(internal error) concat expected atom, got pair" - ); - - assert_eq!( - a.new_concat(4, &[atom1, atom2]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); - - assert_eq!( - a.new_concat(2, &[atom1, atom2]).unwrap_err().1, - "(internal error) concat passed invalid new_size" - ); -} + #[test] + fn test_concat_launder_small_number() { + let mut a = Allocator::new(); + let atom1 = a.new_small_number(42).expect("new_small_number"); + assert_eq!(a.small_number(atom1), Some(42)); + + // this "launders" the small number into actually being allocated on the + // heap + let atom2 = a + .new_concat(1, &[a.nil(), atom1, a.nil()]) + .expect("new_substr"); + + // even though this atom is allocated on the heap (and not stored as a small + // int), we can still retrieve it as one. The CLVM interpreter depends on + // this when matching operators against quote, apply and softfork. + assert_eq!(a.small_number(atom2), Some(42)); + assert_eq!(a.atom_len(atom2), 1); + assert_eq!(a.atom(atom2).as_ref(), &[42]); + } -#[test] -fn test_sexp() { - let mut a = Allocator::new(); - let atom1 = a.new_atom(b"f").unwrap(); - let atom2 = a.new_atom(b"o").unwrap(); - let pair = a.new_pair(atom1, atom2).unwrap(); + #[test] + fn test_concat() { + let mut a = Allocator::new(); + let atom1 = a.new_atom(b"f").unwrap(); + let atom2 = a.new_atom(b"o").unwrap(); + let atom3 = a.new_atom(b"o").unwrap(); + let atom4 = a.new_atom(b"b").unwrap(); + let atom5 = a.new_atom(b"a").unwrap(); + let atom6 = a.new_atom(b"r").unwrap(); + let pair = a.new_pair(atom1, atom2).unwrap(); + + let cat = a + .new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6]) + .unwrap(); + assert_eq!(a.atom(cat).as_ref(), b"foobar"); + + let cat = a.new_concat(12, &[cat, cat]).unwrap(); + assert_eq!(a.atom(cat).as_ref(), b"foobarfoobar"); + + assert_eq!( + a.new_concat(11, &[cat, cat]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + assert_eq!( + a.new_concat(13, &[cat, cat]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + assert_eq!( + a.new_concat(12, &[atom3, pair]).unwrap_err().1, + "(internal error) concat expected atom, got pair" + ); + + assert_eq!( + a.new_concat(4, &[atom1, atom2, atom3]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + + assert_eq!( + a.new_concat(2, &[atom1, atom2, atom3]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + } - assert_eq!(a.sexp(atom1), SExp::Atom); - assert_eq!(a.sexp(atom2), SExp::Atom); - assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2)); -} + #[test] + fn test_concat_large() { + let mut a = Allocator::new(); + let atom1 = a.new_atom(b"foo").unwrap(); + let atom2 = a.new_atom(b"bar").unwrap(); + let pair = a.new_pair(atom1, atom2).unwrap(); + + let cat = a.new_concat(6, &[atom1, atom2]).unwrap(); + assert_eq!(a.atom(cat).as_ref(), b"foobar"); + + let cat = a.new_concat(12, &[cat, cat]).unwrap(); + assert_eq!(a.atom(cat).as_ref(), b"foobarfoobar"); + + assert_eq!( + a.new_concat(11, &[cat, cat]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + assert_eq!( + a.new_concat(13, &[cat, cat]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + assert_eq!( + a.new_concat(12, &[atom1, pair]).unwrap_err().1, + "(internal error) concat expected atom, got pair" + ); + + assert_eq!( + a.new_concat(4, &[atom1, atom2]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + + assert_eq!( + a.new_concat(2, &[atom1, atom2]).unwrap_err().1, + "(internal error) concat passed invalid new_size" + ); + } -#[test] -fn test_concat_limit() { - let mut a = Allocator::new_limited(6); - let atom1 = a.new_atom(b"f").unwrap(); - let atom2 = a.new_atom(b"o").unwrap(); - let atom3 = a.new_atom(b"o").unwrap(); - let atom4 = a.new_atom(b"b").unwrap(); - let atom5 = a.new_atom(b"a").unwrap(); - let atom6 = a.new_atom(b"r").unwrap(); - - // we only have 2 bytes left of allowed heap allocation - assert_eq!( - a.new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6]) - .unwrap_err() - .1, - "out of memory" - ); - let cat = a.new_concat(2, &[atom1, atom2]).unwrap(); - assert_eq!(a.atom(cat).as_ref(), b"fo"); -} + #[test] + fn test_sexp() { + let mut a = Allocator::new(); + let atom1 = a.new_atom(b"f").unwrap(); + let atom2 = a.new_atom(b"o").unwrap(); + let pair = a.new_pair(atom1, atom2).unwrap(); -#[cfg(test)] -use rstest::rstest; + assert_eq!(a.sexp(atom1), SExp::Atom); + assert_eq!(a.sexp(atom2), SExp::Atom); + assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2)); + } -#[cfg(test)] -#[rstest] -#[case(0.into(), &[])] -#[case(1.into(), &[1])] -#[case((-1).into(), &[0xff])] -#[case(0x80.into(), &[0, 0x80])] -#[case(0xff.into(), &[0, 0xff])] -#[case(0xffffffff_u64.into(), &[0, 0xff, 0xff, 0xff, 0xff])] -fn test_new_number(#[case] num: Number, #[case] expected: &[u8]) { - let mut a = Allocator::new(); - - // TEST creating the atom from a Number - let atom = a.new_number(num.clone()).unwrap(); - - // make sure we get back the same number - assert_eq!(a.number(atom), num); - assert_eq!(a.atom(atom).as_ref(), expected); - assert_eq!(number_from_u8(expected), num); - - // TEST creating the atom from a buffer - let atom = a.new_atom(expected).unwrap(); - - // make sure we get back the same number - assert_eq!(a.number(atom), num); - assert_eq!(a.atom(atom).as_ref(), expected); - assert_eq!(number_from_u8(expected), num); -} + #[test] + fn test_concat_limit() { + let mut a = Allocator::new_limited(6); + let atom1 = a.new_atom(b"f").unwrap(); + let atom2 = a.new_atom(b"o").unwrap(); + let atom3 = a.new_atom(b"o").unwrap(); + let atom4 = a.new_atom(b"b").unwrap(); + let atom5 = a.new_atom(b"a").unwrap(); + let atom6 = a.new_atom(b"r").unwrap(); + + // we only have 2 bytes left of allowed heap allocation + assert_eq!( + a.new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6]) + .unwrap_err() + .1, + "out of memory" + ); + let cat = a.new_concat(2, &[atom1, atom2]).unwrap(); + assert_eq!(a.atom(cat).as_ref(), b"fo"); + } -#[test] -fn test_checkpoints() { - let mut a = Allocator::new(); + #[rstest] + #[case(0.into(), &[])] + #[case(1.into(), &[1])] + #[case((-1).into(), &[0xff])] + #[case(0x80.into(), &[0, 0x80])] + #[case(0xff.into(), &[0, 0xff])] + #[case(0xffffffff_u64.into(), &[0, 0xff, 0xff, 0xff, 0xff])] + fn test_new_number(#[case] num: Number, #[case] expected: &[u8]) { + let mut a = Allocator::new(); + + // TEST creating the atom from a Number + let atom = a.new_number(num.clone()).unwrap(); + + // make sure we get back the same number + assert_eq!(a.number(atom), num); + assert_eq!(a.atom(atom).as_ref(), expected); + assert_eq!(number_from_u8(expected), num); + + // TEST creating the atom from a buffer + let atom = a.new_atom(expected).unwrap(); + + // make sure we get back the same number + assert_eq!(a.number(atom), num); + assert_eq!(a.atom(atom).as_ref(), expected); + assert_eq!(number_from_u8(expected), num); + } - let atom1 = a.new_atom(&[4, 3, 2, 1]).unwrap(); - assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]); + #[test] + fn test_checkpoints() { + let mut a = Allocator::new(); - let checkpoint = a.checkpoint(); + let atom1 = a.new_atom(&[4, 3, 2, 1]).unwrap(); + assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]); - let atom2 = a.new_atom(&[6, 5, 4, 3]).unwrap(); - assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]); - assert!(a.atom(atom2).as_ref() == [6, 5, 4, 3]); + let checkpoint = a.checkpoint(); - // at this point we have two atoms and a checkpoint from before the second - // atom was created + let atom2 = a.new_atom(&[6, 5, 4, 3]).unwrap(); + assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]); + assert!(a.atom(atom2).as_ref() == [6, 5, 4, 3]); - // now, restoring the checkpoint state will make atom2 disappear + // at this point we have two atoms and a checkpoint from before the second + // atom was created - a.restore_checkpoint(&checkpoint); + // now, restoring the checkpoint state will make atom2 disappear - assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]); - let atom3 = a.new_atom(&[6, 5, 4, 3]).unwrap(); - assert!(a.atom(atom3).as_ref() == [6, 5, 4, 3]); + a.restore_checkpoint(&checkpoint); - // since atom2 was removed, atom3 should actually be using that slot - assert_eq!(atom2, atom3); -} + assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]); + let atom3 = a.new_atom(&[6, 5, 4, 3]).unwrap(); + assert!(a.atom(atom3).as_ref() == [6, 5, 4, 3]); -#[cfg(test)] -fn test_g1(a: &Allocator, n: NodePtr) -> EvalErr { - a.g1(n).unwrap_err() -} + // since atom2 was removed, atom3 should actually be using that slot + assert_eq!(atom2, atom3); + } -#[cfg(test)] -fn test_g2(a: &Allocator, n: NodePtr) -> EvalErr { - a.g2(n).unwrap_err() -} + fn test_g1(a: &Allocator, n: NodePtr) -> EvalErr { + a.g1(n).unwrap_err() + } -#[cfg(test)] -type TestFun = fn(&Allocator, NodePtr) -> EvalErr; + fn test_g2(a: &Allocator, n: NodePtr) -> EvalErr { + a.g2(n).unwrap_err() + } -#[cfg(test)] -#[rstest] -#[case(test_g1, 0, "atom is not G1 size, 48 bytes")] -#[case(test_g1, 3, "atom is not G1 size, 48 bytes")] -#[case(test_g1, 47, "atom is not G1 size, 48 bytes")] -#[case(test_g1, 49, "atom is not G1 size, 48 bytes")] -#[case(test_g1, 48, "atom is not a G1 point")] -#[case(test_g2, 0, "atom is not G2 size, 96 bytes")] -#[case(test_g2, 3, "atom is not G2 size, 96 bytes")] -#[case(test_g2, 95, "atom is not G2 size, 96 bytes")] -#[case(test_g2, 97, "atom is not G2 size, 96 bytes")] -#[case(test_g2, 96, "atom is not a G2 point")] -fn test_point_size_error(#[case] fun: TestFun, #[case] size: usize, #[case] expected: &str) { - let mut a = Allocator::new(); - let mut buf = Vec::::new(); - buf.resize(size, 0xcc); - let n = a.new_atom(&buf).unwrap(); - let r = fun(&a, n); - assert_eq!(r.0, n); - assert_eq!(r.1, expected.to_string()); -} + type TestFun = fn(&Allocator, NodePtr) -> EvalErr; + + #[rstest] + #[case(test_g1, 0, "atom is not G1 size, 48 bytes")] + #[case(test_g1, 3, "atom is not G1 size, 48 bytes")] + #[case(test_g1, 47, "atom is not G1 size, 48 bytes")] + #[case(test_g1, 49, "atom is not G1 size, 48 bytes")] + #[case(test_g1, 48, "atom is not a G1 point")] + #[case(test_g2, 0, "atom is not G2 size, 96 bytes")] + #[case(test_g2, 3, "atom is not G2 size, 96 bytes")] + #[case(test_g2, 95, "atom is not G2 size, 96 bytes")] + #[case(test_g2, 97, "atom is not G2 size, 96 bytes")] + #[case(test_g2, 96, "atom is not a G2 point")] + fn test_point_size_error(#[case] fun: TestFun, #[case] size: usize, #[case] expected: &str) { + let mut a = Allocator::new(); + let mut buf = Vec::::new(); + buf.resize(size, 0xcc); + let n = a.new_atom(&buf).unwrap(); + let r = fun(&a, n); + assert_eq!(r.0, n); + assert_eq!(r.1, expected.to_string()); + } -#[cfg(test)] -#[rstest] -#[case(test_g1, "pair found, expected G1 point")] -#[case(test_g2, "pair found, expected G2 point")] -fn test_point_atom_pair(#[case] fun: TestFun, #[case] expected: &str) { - let mut a = Allocator::new(); - let n = a.new_pair(a.nil(), a.one()).unwrap(); - let r = fun(&a, n); - assert_eq!(r.0, n); - assert_eq!(r.1, expected.to_string()); -} + #[rstest] + #[case(test_g1, "pair found, expected G1 point")] + #[case(test_g2, "pair found, expected G2 point")] + fn test_point_atom_pair(#[case] fun: TestFun, #[case] expected: &str) { + let mut a = Allocator::new(); + let n = a.new_pair(a.nil(), a.one()).unwrap(); + let r = fun(&a, n); + assert_eq!(r.0, n); + assert_eq!(r.1, expected.to_string()); + } -#[cfg(test)] -#[rstest] -#[case( - "\ + #[rstest] + #[case( + "\ 97f1d3a73197d7942695638c4fa9ac0f\ c3688c4f9774b905a14e3a3f171bac58\ 6c55e83ff97a1aeffb3af00adb22c6bb" -)] -#[case( - "\ + )] + #[case( + "\ a572cbea904d67468808c8eb50a9450c\ 9721db309128012543902d0ac358a62a\ e28f75bb8f1c7c42c39a8c5529bf0f4e" -)] -fn test_g1_roundtrip(#[case] atom: &str) { - let mut a = Allocator::new(); - let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap(); - let g1 = a.g1(n).unwrap(); - assert_eq!(hex::encode(g1.to_bytes()), atom); - - let g1_copy = a.new_g1(g1).unwrap(); - let g1_atom = a.atom(g1_copy); - assert_eq!(hex::encode(g1_atom), atom); - - // try interpreting the point as G1 - assert_eq!(a.g2(n).unwrap_err().1, "atom is not G2 size, 96 bytes"); - assert_eq!( - a.g2(g1_copy).unwrap_err().1, - "atom is not G2 size, 96 bytes" - ); - - // try interpreting the point as number - assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap())); - assert_eq!( - a.number(g1_copy), - number_from_u8(&hex::decode(atom).unwrap()) - ); -} + )] + fn test_g1_roundtrip(#[case] atom: &str) { + let mut a = Allocator::new(); + let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap(); + let g1 = a.g1(n).unwrap(); + assert_eq!(hex::encode(g1.to_bytes()), atom); + + let g1_copy = a.new_g1(g1).unwrap(); + let g1_atom = a.atom(g1_copy); + assert_eq!(hex::encode(g1_atom), atom); + + // try interpreting the point as G1 + assert_eq!(a.g2(n).unwrap_err().1, "atom is not G2 size, 96 bytes"); + assert_eq!( + a.g2(g1_copy).unwrap_err().1, + "atom is not G2 size, 96 bytes" + ); + + // try interpreting the point as number + assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap())); + assert_eq!( + a.number(g1_copy), + number_from_u8(&hex::decode(atom).unwrap()) + ); + } -#[cfg(test)] -#[rstest] -#[case( - "\ + #[rstest] + #[case( + "\ 93e02b6052719f607dacd3a088274f65\ 596bd0d09920b61ab5da61bbdc7f5049\ 334cf11213945d57e5ac7d055d042b7e\ 024aa2b2f08f0a91260805272dc51051\ c6e47ad4fa403b02b4510b647ae3d177\ 0bac0326a805bbefd48056c8c121bdb8" -)] -#[case( - "\ + )] + #[case( + "\ aa4edef9c1ed7f729f520e47730a124f\ d70662a904ba1074728114d1031e1572\ c6c886f6b57ec72a6178288c47c33577\ 1638533957d540a9d2370f17cc7ed586\ 3bc0b995b8825e0ee1ea1e1e4d00dbae\ 81f14b0bf3611b78c952aacab827a053" -)] -fn test_g2_roundtrip(#[case] atom: &str) { - let mut a = Allocator::new(); - let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap(); - let g2 = a.g2(n).unwrap(); - assert_eq!(hex::encode(g2.to_bytes()), atom); - - let g2_copy = a.new_g2(g2).unwrap(); - let g2_atom = a.atom(g2_copy); - assert_eq!(hex::encode(g2_atom), atom); - - // try interpreting the point as G1 - assert_eq!(a.g1(n).unwrap_err().1, "atom is not G1 size, 48 bytes"); - assert_eq!( - a.g1(g2_copy).unwrap_err().1, - "atom is not G1 size, 48 bytes" - ); - - // try interpreting the point as number - assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap())); - assert_eq!( - a.number(g2_copy), - number_from_u8(&hex::decode(atom).unwrap()) - ); -} + )] + fn test_g2_roundtrip(#[case] atom: &str) { + let mut a = Allocator::new(); + let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap(); + let g2 = a.g2(n).unwrap(); + assert_eq!(hex::encode(g2.to_bytes()), atom); + + let g2_copy = a.new_g2(g2).unwrap(); + let g2_atom = a.atom(g2_copy); + assert_eq!(hex::encode(g2_atom), atom); + + // try interpreting the point as G1 + assert_eq!(a.g1(n).unwrap_err().1, "atom is not G1 size, 48 bytes"); + assert_eq!( + a.g1(g2_copy).unwrap_err().1, + "atom is not G1 size, 48 bytes" + ); + + // try interpreting the point as number + assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap())); + assert_eq!( + a.number(g2_copy), + number_from_u8(&hex::decode(atom).unwrap()) + ); + } -use std::borrow::Borrow; + type MakeFun = fn(&mut Allocator, &[u8]) -> NodePtr; -#[cfg(test)] -type MakeFun = fn(&mut Allocator, &[u8]) -> NodePtr; - -#[cfg(test)] -fn make_buf(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - a.new_atom(bytes).unwrap() -} + fn make_buf(a: &mut Allocator, bytes: &[u8]) -> NodePtr { + a.new_atom(bytes).unwrap() + } -#[cfg(test)] -fn make_number(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - let v = number_from_u8(bytes); - a.new_number(v).unwrap() -} + fn make_number(a: &mut Allocator, bytes: &[u8]) -> NodePtr { + let v = number_from_u8(bytes); + a.new_number(v).unwrap() + } -#[cfg(test)] -fn make_g1(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap(); - a.new_g1(v).unwrap() -} + fn make_g1(a: &mut Allocator, bytes: &[u8]) -> NodePtr { + let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap(); + a.new_g1(v).unwrap() + } -#[cfg(test)] -fn make_g2(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap(); - a.new_g2(v).unwrap() -} + fn make_g2(a: &mut Allocator, bytes: &[u8]) -> NodePtr { + let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap(); + a.new_g2(v).unwrap() + } -#[cfg(test)] -fn make_g1_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - assert!(<[u8; 48]>::try_from(bytes).is_err()); - a.new_atom(bytes).unwrap() -} + fn make_g1_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr { + assert!(<[u8; 48]>::try_from(bytes).is_err()); + a.new_atom(bytes).unwrap() + } -#[cfg(test)] -fn make_g2_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - assert!(<[u8; 96]>::try_from(bytes).is_err()); - a.new_atom(bytes).unwrap() -} + fn make_g2_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr { + assert!(<[u8; 96]>::try_from(bytes).is_err()); + a.new_atom(bytes).unwrap() + } -#[cfg(test)] -type CheckFun = fn(&Allocator, NodePtr, &[u8]); + type CheckFun = fn(&Allocator, NodePtr, &[u8]); -#[cfg(test)] -fn check_buf(a: &Allocator, n: NodePtr, bytes: &[u8]) { - let buf = a.atom(n); - assert_eq!(buf.as_ref(), bytes); -} + fn check_buf(a: &Allocator, n: NodePtr, bytes: &[u8]) { + let buf = a.atom(n); + assert_eq!(buf.as_ref(), bytes); + } -#[cfg(test)] -fn check_number(a: &Allocator, n: NodePtr, bytes: &[u8]) { - let num = a.number(n); - let v = number_from_u8(bytes); - assert_eq!(num, v); -} + fn check_number(a: &Allocator, n: NodePtr, bytes: &[u8]) { + let num = a.number(n); + let v = number_from_u8(bytes); + assert_eq!(num, v); + } -#[cfg(test)] -fn check_g1(a: &Allocator, n: NodePtr, bytes: &[u8]) { - let num = a.g1(n).unwrap(); - let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap(); - assert_eq!(num, v); -} + fn check_g1(a: &Allocator, n: NodePtr, bytes: &[u8]) { + let num = a.g1(n).unwrap(); + let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap(); + assert_eq!(num, v); + } -#[cfg(test)] -fn check_g2(a: &Allocator, n: NodePtr, bytes: &[u8]) { - let num = a.g2(n).unwrap(); - let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap(); - assert_eq!(num, v); -} + fn check_g2(a: &Allocator, n: NodePtr, bytes: &[u8]) { + let num = a.g2(n).unwrap(); + let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap(); + assert_eq!(num, v); + } -#[cfg(test)] -fn check_g1_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) { - assert_eq!(a.g1(n).unwrap_err().0, n); - assert!(<[u8; 48]>::try_from(bytes).is_err()); -} + fn check_g1_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) { + assert_eq!(a.g1(n).unwrap_err().0, n); + assert!(<[u8; 48]>::try_from(bytes).is_err()); + } -#[cfg(test)] -fn check_g2_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) { - assert_eq!(a.g2(n).unwrap_err().0, n); - assert!(<[u8; 96]>::try_from(bytes).is_err()); -} + fn check_g2_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) { + assert_eq!(a.g2(n).unwrap_err().0, n); + assert!(<[u8; 96]>::try_from(bytes).is_err()); + } -#[cfg(test)] -const EMPTY: &str = ""; + const EMPTY: &str = ""; -#[cfg(test)] -const SMALL_BUF: &str = "133742"; + const SMALL_BUF: &str = "133742"; -#[cfg(test)] -const VALID_G1: &str = "\ + const VALID_G1: &str = "\ a572cbea904d67468808c8eb50a9450c\ 9721db309128012543902d0ac358a62a\ e28f75bb8f1c7c42c39a8c5529bf0f4e"; -#[cfg(test)] -const VALID_G2: &str = "\ + const VALID_G2: &str = "\ aa4edef9c1ed7f729f520e47730a124f\ d70662a904ba1074728114d1031e1572\ c6c886f6b57ec72a6178288c47c33577\ @@ -1492,388 +1467,378 @@ c6c886f6b57ec72a6178288c47c33577\ 3bc0b995b8825e0ee1ea1e1e4d00dbae\ 81f14b0bf3611b78c952aacab827a053"; -/* - We want to exercise round-tripping avery kind of value via every other kind - of value (as far as possible). e.g. Every value can round-trip through a byte buffer - or a number, but G1 cannot round-trip via G2. - - +-----------+--------+--------+------+------+ - | from / to | buffer | number | G1 | G2 | - +-----------+--------+--------+------+------+ - | buffer | o | o | - | - | - | number | o | o | - | - | - | G1 | o | o | o | - | - | G2 | o | o | - | o | - +-----------+--------+--------+------+------+ - -*/ - -#[cfg(test)] -#[rstest] -// round trip empty buffer -#[case(EMPTY, make_buf, check_buf)] -#[case(EMPTY, make_buf, check_number)] -#[case(EMPTY, make_buf, check_g1_fail)] -#[case(EMPTY, make_buf, check_g2_fail)] -#[case(EMPTY, make_number, check_buf)] -#[case(EMPTY, make_number, check_number)] -#[case(EMPTY, make_number, check_g1_fail)] -#[case(EMPTY, make_number, check_g2_fail)] -#[case(EMPTY, make_g1_fail, check_buf)] -#[case(EMPTY, make_g1_fail, check_number)] -#[case(EMPTY, make_g1_fail, check_g1_fail)] -#[case(EMPTY, make_g1_fail, check_g2_fail)] -#[case(EMPTY, make_g2_fail, check_buf)] -#[case(EMPTY, make_g2_fail, check_number)] -#[case(EMPTY, make_g2_fail, check_g1_fail)] -#[case(EMPTY, make_g2_fail, check_g2_fail)] -// round trip small buffer -#[case(SMALL_BUF, make_buf, check_buf)] -#[case(SMALL_BUF, make_buf, check_number)] -#[case(SMALL_BUF, make_buf, check_g1_fail)] -#[case(SMALL_BUF, make_buf, check_g2_fail)] -#[case(SMALL_BUF, make_number, check_buf)] -#[case(SMALL_BUF, make_number, check_number)] -#[case(SMALL_BUF, make_number, check_g1_fail)] -#[case(SMALL_BUF, make_number, check_g2_fail)] -#[case(SMALL_BUF, make_g1_fail, check_buf)] -#[case(SMALL_BUF, make_g1_fail, check_number)] -#[case(SMALL_BUF, make_g1_fail, check_g1_fail)] -#[case(SMALL_BUF, make_g1_fail, check_g2_fail)] -#[case(SMALL_BUF, make_g2_fail, check_buf)] -#[case(SMALL_BUF, make_g2_fail, check_number)] -#[case(SMALL_BUF, make_g2_fail, check_g1_fail)] -#[case(SMALL_BUF, make_g2_fail, check_g2_fail)] -// round trip G1 point -#[case(VALID_G1, make_buf, check_buf)] -#[case(VALID_G1, make_buf, check_number)] -#[case(VALID_G1, make_buf, check_g1)] -#[case(VALID_G1, make_buf, check_g2_fail)] -#[case(VALID_G1, make_number, check_buf)] -#[case(VALID_G1, make_number, check_number)] -#[case(VALID_G1, make_number, check_g1)] -#[case(VALID_G1, make_number, check_g2_fail)] -#[case(VALID_G1, make_g1, check_buf)] -#[case(VALID_G1, make_g1, check_number)] -#[case(VALID_G1, make_g1, check_g1)] -#[case(VALID_G1, make_g1, check_g2_fail)] -#[case(VALID_G1, make_g2_fail, check_buf)] -#[case(VALID_G1, make_g2_fail, check_number)] -#[case(VALID_G1, make_g2_fail, check_g1)] -#[case(VALID_G1, make_g2_fail, check_g2_fail)] -// round trip G2 point -#[case(VALID_G2, make_buf, check_buf)] -#[case(VALID_G2, make_buf, check_number)] -#[case(VALID_G2, make_buf, check_g1_fail)] -#[case(VALID_G2, make_buf, check_g2)] -#[case(VALID_G2, make_number, check_buf)] -#[case(VALID_G2, make_number, check_number)] -#[case(VALID_G2, make_number, check_g1_fail)] -#[case(VALID_G2, make_number, check_g2)] -#[case(VALID_G2, make_g1_fail, check_buf)] -#[case(VALID_G2, make_g1_fail, check_number)] -#[case(VALID_G2, make_g1_fail, check_g1_fail)] -#[case(VALID_G2, make_g1_fail, check_g2)] -#[case(VALID_G2, make_g2, check_buf)] -#[case(VALID_G2, make_g2, check_number)] -#[case(VALID_G2, make_g2, check_g1_fail)] -#[case(VALID_G2, make_g2, check_g2)] -fn test_roundtrip(#[case] test_value: &str, #[case] make: MakeFun, #[case] check: CheckFun) { - let value = hex::decode(test_value).unwrap(); - let mut a = Allocator::new(); - let node = make(&mut a, &value); - check(&a, node, &value); -} + /* + We want to exercise round-tripping avery kind of value via every other kind + of value (as far as possible). e.g. Every value can round-trip through a byte buffer + or a number, but G1 cannot round-trip via G2. + + +-----------+--------+--------+------+------+ + | from / to | buffer | number | G1 | G2 | + +-----------+--------+--------+------+------+ + | buffer | o | o | - | - | + | number | o | o | - | - | + | G1 | o | o | o | - | + | G2 | o | o | - | o | + +-----------+--------+--------+------+------+ + + */ + + #[rstest] + // round trip empty buffer + #[case(EMPTY, make_buf, check_buf)] + #[case(EMPTY, make_buf, check_number)] + #[case(EMPTY, make_buf, check_g1_fail)] + #[case(EMPTY, make_buf, check_g2_fail)] + #[case(EMPTY, make_number, check_buf)] + #[case(EMPTY, make_number, check_number)] + #[case(EMPTY, make_number, check_g1_fail)] + #[case(EMPTY, make_number, check_g2_fail)] + #[case(EMPTY, make_g1_fail, check_buf)] + #[case(EMPTY, make_g1_fail, check_number)] + #[case(EMPTY, make_g1_fail, check_g1_fail)] + #[case(EMPTY, make_g1_fail, check_g2_fail)] + #[case(EMPTY, make_g2_fail, check_buf)] + #[case(EMPTY, make_g2_fail, check_number)] + #[case(EMPTY, make_g2_fail, check_g1_fail)] + #[case(EMPTY, make_g2_fail, check_g2_fail)] + // round trip small buffer + #[case(SMALL_BUF, make_buf, check_buf)] + #[case(SMALL_BUF, make_buf, check_number)] + #[case(SMALL_BUF, make_buf, check_g1_fail)] + #[case(SMALL_BUF, make_buf, check_g2_fail)] + #[case(SMALL_BUF, make_number, check_buf)] + #[case(SMALL_BUF, make_number, check_number)] + #[case(SMALL_BUF, make_number, check_g1_fail)] + #[case(SMALL_BUF, make_number, check_g2_fail)] + #[case(SMALL_BUF, make_g1_fail, check_buf)] + #[case(SMALL_BUF, make_g1_fail, check_number)] + #[case(SMALL_BUF, make_g1_fail, check_g1_fail)] + #[case(SMALL_BUF, make_g1_fail, check_g2_fail)] + #[case(SMALL_BUF, make_g2_fail, check_buf)] + #[case(SMALL_BUF, make_g2_fail, check_number)] + #[case(SMALL_BUF, make_g2_fail, check_g1_fail)] + #[case(SMALL_BUF, make_g2_fail, check_g2_fail)] + // round trip G1 point + #[case(VALID_G1, make_buf, check_buf)] + #[case(VALID_G1, make_buf, check_number)] + #[case(VALID_G1, make_buf, check_g1)] + #[case(VALID_G1, make_buf, check_g2_fail)] + #[case(VALID_G1, make_number, check_buf)] + #[case(VALID_G1, make_number, check_number)] + #[case(VALID_G1, make_number, check_g1)] + #[case(VALID_G1, make_number, check_g2_fail)] + #[case(VALID_G1, make_g1, check_buf)] + #[case(VALID_G1, make_g1, check_number)] + #[case(VALID_G1, make_g1, check_g1)] + #[case(VALID_G1, make_g1, check_g2_fail)] + #[case(VALID_G1, make_g2_fail, check_buf)] + #[case(VALID_G1, make_g2_fail, check_number)] + #[case(VALID_G1, make_g2_fail, check_g1)] + #[case(VALID_G1, make_g2_fail, check_g2_fail)] + // round trip G2 point + #[case(VALID_G2, make_buf, check_buf)] + #[case(VALID_G2, make_buf, check_number)] + #[case(VALID_G2, make_buf, check_g1_fail)] + #[case(VALID_G2, make_buf, check_g2)] + #[case(VALID_G2, make_number, check_buf)] + #[case(VALID_G2, make_number, check_number)] + #[case(VALID_G2, make_number, check_g1_fail)] + #[case(VALID_G2, make_number, check_g2)] + #[case(VALID_G2, make_g1_fail, check_buf)] + #[case(VALID_G2, make_g1_fail, check_number)] + #[case(VALID_G2, make_g1_fail, check_g1_fail)] + #[case(VALID_G2, make_g1_fail, check_g2)] + #[case(VALID_G2, make_g2, check_buf)] + #[case(VALID_G2, make_g2, check_number)] + #[case(VALID_G2, make_g2, check_g1_fail)] + #[case(VALID_G2, make_g2, check_g2)] + fn test_roundtrip(#[case] test_value: &str, #[case] make: MakeFun, #[case] check: CheckFun) { + let value = hex::decode(test_value).unwrap(); + let mut a = Allocator::new(); + let node = make(&mut a, &value); + check(&a, node, &value); + } -#[cfg(test)] -#[rstest] -#[case(&[], 0)] -#[case(&[1], 1)] -#[case(&[1,2], 2)] -#[case(&[1,2,3,4,5,6,7,8,9], 9)] -#[case(&[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], 18)] -fn test_atom_len(#[case] buf: &[u8], #[case] expected: usize) { - let mut a = Allocator::new(); - let atom = a.new_atom(buf).unwrap(); - assert_eq!(a.atom_len(atom), expected); -} + #[rstest] + #[case(&[], 0)] + #[case(&[1], 1)] + #[case(&[1,2], 2)] + #[case(&[1,2,3,4,5,6,7,8,9], 9)] + #[case(&[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], 18)] + fn test_atom_len(#[case] buf: &[u8], #[case] expected: usize) { + let mut a = Allocator::new(); + let atom = a.new_atom(buf).unwrap(); + assert_eq!(a.atom_len(atom), expected); + } -#[cfg(test)] -#[rstest] -#[case(0.into(), 0)] -#[case(42.into(), 1)] -#[case(127.into(), 1)] -#[case(1337.into(), 2)] -#[case(0x7fffff.into(), 3)] -#[case(0xffffff.into(), 4)] -#[case((-1).into(), 1)] -#[case((-128).into(), 1)] -fn test_atom_len_number(#[case] value: Number, #[case] expected: usize) { - let mut a = Allocator::new(); - let atom = a.new_number(value).unwrap(); - assert_eq!(a.atom_len(atom), expected); -} + #[rstest] + #[case(0.into(), 0)] + #[case(42.into(), 1)] + #[case(127.into(), 1)] + #[case(1337.into(), 2)] + #[case(0x7fffff.into(), 3)] + #[case(0xffffff.into(), 4)] + #[case((-1).into(), 1)] + #[case((-128).into(), 1)] + fn test_atom_len_number(#[case] value: Number, #[case] expected: usize) { + let mut a = Allocator::new(); + let atom = a.new_number(value).unwrap(); + assert_eq!(a.atom_len(atom), expected); + } -#[cfg(test)] -#[rstest] -#[case( - "\ + #[rstest] + #[case( + "\ 97f1d3a73197d7942695638c4fa9ac0f\ c3688c4f9774b905a14e3a3f171bac58\ 6c55e83ff97a1aeffb3af00adb22c6bb", - 48 -)] -#[case( - "\ + 48 + )] + #[case( + "\ a572cbea904d67468808c8eb50a9450c\ 9721db309128012543902d0ac358a62a\ e28f75bb8f1c7c42c39a8c5529bf0f4e", - 48 -)] -fn test_atom_len_g1(#[case] buffer_hex: &str, #[case] expected: usize) { - let mut a = Allocator::new(); - let buffer = &hex::decode(buffer_hex).unwrap(); - let g1 = G1Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G1 point"); - let atom = a.new_g1(g1).unwrap(); - assert_eq!(a.atom_len(atom), expected); -} + 48 + )] + fn test_atom_len_g1(#[case] buffer_hex: &str, #[case] expected: usize) { + let mut a = Allocator::new(); + let buffer = &hex::decode(buffer_hex).unwrap(); + let g1 = G1Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G1 point"); + let atom = a.new_g1(g1).unwrap(); + assert_eq!(a.atom_len(atom), expected); + } -#[cfg(test)] -#[rstest] -#[case( - "\ + #[rstest] + #[case( + "\ 93e02b6052719f607dacd3a088274f65\ 596bd0d09920b61ab5da61bbdc7f5049\ 334cf11213945d57e5ac7d055d042b7e\ 024aa2b2f08f0a91260805272dc51051\ c6e47ad4fa403b02b4510b647ae3d177\ 0bac0326a805bbefd48056c8c121bdb8", - 96 -)] -#[case( - "\ + 96 + )] + #[case( + "\ aa4edef9c1ed7f729f520e47730a124f\ d70662a904ba1074728114d1031e1572\ c6c886f6b57ec72a6178288c47c33577\ 1638533957d540a9d2370f17cc7ed586\ 3bc0b995b8825e0ee1ea1e1e4d00dbae\ 81f14b0bf3611b78c952aacab827a053", - 96 -)] -fn test_atom_len_g2(#[case] buffer_hex: &str, #[case] expected: usize) { - let mut a = Allocator::new(); - - let buffer = &hex::decode(buffer_hex).unwrap(); - let g2 = G2Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G2 point"); - let atom = a.new_g2(g2).unwrap(); - assert_eq!(a.atom_len(atom), expected); -} - -#[cfg(test)] -#[rstest] -#[case(0.into())] -#[case(1.into())] -#[case(0x7f.into())] -#[case(0x80.into())] -#[case(0xff.into())] -#[case(0x100.into())] -#[case(0x7fff.into())] -#[case(0x8000.into())] -#[case(0xffff.into())] -#[case(0x10000.into())] -#[case(0x7ffff.into())] -#[case(0x80000.into())] -#[case(0xfffff.into())] -#[case(0x100000.into())] -#[case(0x7ffffff.into())] -#[case(0x8000000.into())] -#[case(0xfffffff.into())] -#[case(0x10000000.into())] -#[case(0x7ffffffff_u64.into())] -#[case(0x8000000000_u64.into())] -#[case(0xffffffffff_u64.into())] -#[case(0x10000000000_u64.into())] -#[case((-1).into())] -#[case((-0x7f).into())] -#[case((-0x80).into())] -#[case((-0xff).into())] -#[case((-0x100).into())] -#[case((-0x7fff).into())] -#[case((-0x8000).into())] -#[case((-0xffff).into())] -#[case((-0x10000).into())] -#[case((-0x7ffff).into())] -#[case((-0x80000).into())] -#[case((-0xfffff).into())] -#[case((-0x100000).into())] -#[case((-0x7ffffff_i64).into())] -#[case((-0x8000000_i64).into())] -#[case((-0xfffffff_i64).into())] -#[case((-0x10000000_i64).into())] -#[case((-0x7ffffffff_i64).into())] -#[case((-0x8000000000_i64).into())] -#[case((-0xffffffffff_i64).into())] -#[case((-0x10000000000_i64).into())] -fn test_number_roundtrip(#[case] value: Number) { - let mut a = Allocator::new(); - let atom = a.new_number(value.clone()).expect("new_number()"); - assert_eq!(a.number(atom), value); -} + 96 + )] + fn test_atom_len_g2(#[case] buffer_hex: &str, #[case] expected: usize) { + let mut a = Allocator::new(); + + let buffer = &hex::decode(buffer_hex).unwrap(); + let g2 = G2Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G2 point"); + let atom = a.new_g2(g2).unwrap(); + assert_eq!(a.atom_len(atom), expected); + } -#[cfg(test)] -#[rstest] -#[case(0)] -#[case(1)] -#[case(0x7f)] -#[case(0x80)] -#[case(0xff)] -#[case(0x100)] -#[case(0x7fff)] -#[case(0x8000)] -#[case(0xffff)] -#[case(0x10000)] -#[case(0x7ffff)] -#[case(0x80000)] -#[case(0xfffff)] -#[case(0x100000)] -#[case(0x7fffff)] -#[case(0x800000)] -#[case(0xffffff)] -#[case(0x1000000)] -#[case(0x3ffffff)] -fn test_small_number_roundtrip(#[case] value: u32) { - let mut a = Allocator::new(); - let atom = a.new_small_number(value).expect("new_small_number()"); - assert_eq!(a.small_number(atom).expect("small_number()"), value); -} + #[rstest] + #[case(0.into())] + #[case(1.into())] + #[case(0x7f.into())] + #[case(0x80.into())] + #[case(0xff.into())] + #[case(0x100.into())] + #[case(0x7fff.into())] + #[case(0x8000.into())] + #[case(0xffff.into())] + #[case(0x10000.into())] + #[case(0x7ffff.into())] + #[case(0x80000.into())] + #[case(0xfffff.into())] + #[case(0x100000.into())] + #[case(0x7ffffff.into())] + #[case(0x8000000.into())] + #[case(0xfffffff.into())] + #[case(0x10000000.into())] + #[case(0x7ffffffff_u64.into())] + #[case(0x8000000000_u64.into())] + #[case(0xffffffffff_u64.into())] + #[case(0x10000000000_u64.into())] + #[case((-1).into())] + #[case((-0x7f).into())] + #[case((-0x80).into())] + #[case((-0xff).into())] + #[case((-0x100).into())] + #[case((-0x7fff).into())] + #[case((-0x8000).into())] + #[case((-0xffff).into())] + #[case((-0x10000).into())] + #[case((-0x7ffff).into())] + #[case((-0x80000).into())] + #[case((-0xfffff).into())] + #[case((-0x100000).into())] + #[case((-0x7ffffff_i64).into())] + #[case((-0x8000000_i64).into())] + #[case((-0xfffffff_i64).into())] + #[case((-0x10000000_i64).into())] + #[case((-0x7ffffffff_i64).into())] + #[case((-0x8000000000_i64).into())] + #[case((-0xffffffffff_i64).into())] + #[case((-0x10000000000_i64).into())] + fn test_number_roundtrip(#[case] value: Number) { + let mut a = Allocator::new(); + let atom = a.new_number(value.clone()).expect("new_number()"); + assert_eq!(a.number(atom), value); + } -#[cfg(test)] -#[rstest] -#[case(0.into(), true)] -#[case(1.into(), true)] -#[case(0x3ffffff.into(), true)] -#[case(0x4000000.into(), false)] -#[case(0x7f.into(), true)] -#[case(0x80.into(), true)] -#[case(0xff.into(), true)] -#[case(0x100.into(), true)] -#[case(0x7fff.into(), true)] -#[case(0x8000.into(), true)] -#[case(0xffff.into(), true)] -#[case(0x10000.into(), true)] -#[case(0x7ffff.into(), true)] -#[case(0x80000.into(), true)] -#[case(0xfffff.into(), true)] -#[case(0x100000.into(), true)] -#[case(0x7ffffff.into(), false)] -#[case(0x8000000.into(), false)] -#[case(0xfffffff.into(), false)] -#[case(0x10000000.into(), false)] -#[case(0x7ffffffff_u64.into(), false)] -#[case(0x8000000000_u64.into(), false )] -#[case(0xffffffffff_u64.into(), false)] -#[case(0x10000000000_u64.into(), false)] -#[case((-1).into(), false)] -#[case((-0x7f).into(), false)] -#[case((-0x80).into(), false)] -#[case((-0x10000000000_i64).into(), false)] -fn test_auto_small_number(#[case] value: Number, #[case] expect_small: bool) { - let mut a = Allocator::new(); - let atom = a.new_number(value.clone()).expect("new_number()"); - assert_eq!(a.small_number(atom).is_some(), expect_small); - if let Some(v) = a.small_number(atom) { - use num_traits::ToPrimitive; - assert_eq!(v, value.to_u32().unwrap()); + #[rstest] + #[case(0)] + #[case(1)] + #[case(0x7f)] + #[case(0x80)] + #[case(0xff)] + #[case(0x100)] + #[case(0x7fff)] + #[case(0x8000)] + #[case(0xffff)] + #[case(0x10000)] + #[case(0x7ffff)] + #[case(0x80000)] + #[case(0xfffff)] + #[case(0x100000)] + #[case(0x7fffff)] + #[case(0x800000)] + #[case(0xffffff)] + #[case(0x1000000)] + #[case(0x3ffffff)] + fn test_small_number_roundtrip(#[case] value: u32) { + let mut a = Allocator::new(); + let atom = a.new_small_number(value).expect("new_small_number()"); + assert_eq!(a.small_number(atom).expect("small_number()"), value); } - assert_eq!(a.number(atom), value); -} -#[cfg(test)] -#[rstest] -// redundant leading zeros are not canoncial -#[case(&[0x00], false)] -#[case(&[0x00, 0x7f], false)] -// negative numbers cannot be small ints -#[case(&[0x80], false)] -#[case(&[0xff], false)] -#[case(&[0xff, 0xff], false)] -#[case(&[0x80, 0xff, 0xff], false)] -// small positive intergers can be small -#[case(&[0x01], true)] -#[case(&[0x00, 0xff], true)] -#[case(&[0x7f, 0xff], true)] -#[case(&[0x7f, 0xff, 0xff], true)] -#[case(&[0x00, 0xff, 0xff, 0xff], true)] -#[case(&[0x02, 0x00, 0x00, 0x00], true)] -#[case(&[0x03, 0xff, 0xff, 0xff], true)] -// too big -#[case(&[0x04, 0x00, 0x00, 0x00], false)] -fn test_auto_small_number_from_buf(#[case] buf: &[u8], #[case] expect_small: bool) { - let mut a = Allocator::new(); - let atom = a.new_atom(buf).expect("new_atom()"); - assert_eq!(a.small_number(atom).is_some(), expect_small); - if let Some(v) = a.small_number(atom) { - use num_traits::ToPrimitive; - assert_eq!(v, a.number(atom).to_u32().expect("to_u32()")); + #[rstest] + #[case(0.into(), true)] + #[case(1.into(), true)] + #[case(0x3ffffff.into(), true)] + #[case(0x4000000.into(), false)] + #[case(0x7f.into(), true)] + #[case(0x80.into(), true)] + #[case(0xff.into(), true)] + #[case(0x100.into(), true)] + #[case(0x7fff.into(), true)] + #[case(0x8000.into(), true)] + #[case(0xffff.into(), true)] + #[case(0x10000.into(), true)] + #[case(0x7ffff.into(), true)] + #[case(0x80000.into(), true)] + #[case(0xfffff.into(), true)] + #[case(0x100000.into(), true)] + #[case(0x7ffffff.into(), false)] + #[case(0x8000000.into(), false)] + #[case(0xfffffff.into(), false)] + #[case(0x10000000.into(), false)] + #[case(0x7ffffffff_u64.into(), false)] + #[case(0x8000000000_u64.into(), false )] + #[case(0xffffffffff_u64.into(), false)] + #[case(0x10000000000_u64.into(), false)] + #[case((-1).into(), false)] + #[case((-0x7f).into(), false)] + #[case((-0x80).into(), false)] + #[case((-0x10000000000_i64).into(), false)] + fn test_auto_small_number(#[case] value: Number, #[case] expect_small: bool) { + let mut a = Allocator::new(); + let atom = a.new_number(value.clone()).expect("new_number()"); + assert_eq!(a.small_number(atom).is_some(), expect_small); + if let Some(v) = a.small_number(atom) { + use num_traits::ToPrimitive; + assert_eq!(v, value.to_u32().unwrap()); + } + assert_eq!(a.number(atom), value); } - assert_eq!(buf, a.atom(atom).as_ref()); -} -#[cfg(test)] -#[rstest] -// redundant leading zeros are not canoncial -#[case(&[0x00], None)] -#[case(&[0x00, 0x7f], None)] -// negative numbers cannot be small ints -#[case(&[0x80], None)] -#[case(&[0xff], None)] -// redundant leading 0xff are still negative -#[case(&[0xff, 0xff], None)] -#[case(&[0x80, 0xff, 0xff], None)] -// to big -#[case(&[0x04, 0x00, 0x00, 0x00], None)] -#[case(&[0x05, 0x00, 0x00, 0x00], None)] -#[case(&[0x04, 0x00, 0x00, 0x00, 0x00], None)] -// small positive intergers can be small -#[case(&[0x01], Some(0x01))] -#[case(&[0x00, 0x80], Some(0x80))] -#[case(&[0x00, 0xff], Some(0xff))] -#[case(&[0x7f, 0xff], Some(0x7fff))] -#[case(&[0x00, 0x80, 0x00], Some(0x8000))] -#[case(&[0x00, 0xff, 0xff], Some(0xffff))] -#[case(&[0x7f, 0xff, 0xff], Some(0x7fffff))] -#[case(&[0x00, 0x80, 0x00, 0x00], Some(0x800000))] -#[case(&[0x00, 0xff, 0xff, 0xff], Some(0xffffff))] -#[case(&[0x02, 0x00, 0x00, 0x00], Some(0x2000000))] -#[case(&[0x03, 0x00, 0x00, 0x00], Some(0x3000000))] -#[case(&[0x03, 0xff, 0xff, 0xff], Some(0x3ffffff))] -fn test_fits_in_small_atom(#[case] buf: &[u8], #[case] expected: Option) { - assert_eq!(fits_in_small_atom(buf), expected); -} + #[rstest] + // redundant leading zeros are not canoncial + #[case(&[0x00], false)] + #[case(&[0x00, 0x7f], false)] + // negative numbers cannot be small ints + #[case(&[0x80], false)] + #[case(&[0xff], false)] + #[case(&[0xff, 0xff], false)] + #[case(&[0x80, 0xff, 0xff], false)] + // small positive intergers can be small + #[case(&[0x01], true)] + #[case(&[0x00, 0xff], true)] + #[case(&[0x7f, 0xff], true)] + #[case(&[0x7f, 0xff, 0xff], true)] + #[case(&[0x00, 0xff, 0xff, 0xff], true)] + #[case(&[0x02, 0x00, 0x00, 0x00], true)] + #[case(&[0x03, 0xff, 0xff, 0xff], true)] + // too big + #[case(&[0x04, 0x00, 0x00, 0x00], false)] + fn test_auto_small_number_from_buf(#[case] buf: &[u8], #[case] expect_small: bool) { + let mut a = Allocator::new(); + let atom = a.new_atom(buf).expect("new_atom()"); + assert_eq!(a.small_number(atom).is_some(), expect_small); + if let Some(v) = a.small_number(atom) { + use num_traits::ToPrimitive; + assert_eq!(v, a.number(atom).to_u32().expect("to_u32()")); + } + assert_eq!(buf, a.atom(atom).as_ref()); + } -#[cfg(test)] -#[rstest] -// 0 is encoded as an empty string -#[case(&[0], "0", &[])] -#[case(&[1], "1", &[1])] -// leading zeroes are redundant -#[case(&[0,0,0,1], "1", &[1])] -#[case(&[0,0,0x80], "128", &[0, 0x80])] -// A leading zero is necessary to encode a positive number with the -// penultimate byte's most significant bit set -#[case(&[0,0xff], "255", &[0, 0xff])] -#[case(&[0x7f,0xff], "32767", &[0x7f, 0xff])] -// the first byte is redundant, it's still -1 -#[case(&[0xff,0xff], "-1", &[0xff])] -#[case(&[0xff], "-1", &[0xff])] -#[case(&[0,0,0x80,0], "32768", &[0,0x80,0])] -#[case(&[0,0,0x40,0], "16384", &[0x40,0])] -fn test_number_to_atom(#[case] bytes: &[u8], #[case] text: &str, #[case] buf: &[u8]) { - let mut a = Allocator::new(); + #[rstest] + // redundant leading zeros are not canoncial + #[case(&[0x00], None)] + #[case(&[0x00, 0x7f], None)] + // negative numbers cannot be small ints + #[case(&[0x80], None)] + #[case(&[0xff], None)] + // redundant leading 0xff are still negative + #[case(&[0xff, 0xff], None)] + #[case(&[0x80, 0xff, 0xff], None)] + // to big + #[case(&[0x04, 0x00, 0x00, 0x00], None)] + #[case(&[0x05, 0x00, 0x00, 0x00], None)] + #[case(&[0x04, 0x00, 0x00, 0x00, 0x00], None)] + // small positive intergers can be small + #[case(&[0x01], Some(0x01))] + #[case(&[0x00, 0x80], Some(0x80))] + #[case(&[0x00, 0xff], Some(0xff))] + #[case(&[0x7f, 0xff], Some(0x7fff))] + #[case(&[0x00, 0x80, 0x00], Some(0x8000))] + #[case(&[0x00, 0xff, 0xff], Some(0xffff))] + #[case(&[0x7f, 0xff, 0xff], Some(0x7fffff))] + #[case(&[0x00, 0x80, 0x00, 0x00], Some(0x800000))] + #[case(&[0x00, 0xff, 0xff, 0xff], Some(0xffffff))] + #[case(&[0x02, 0x00, 0x00, 0x00], Some(0x2000000))] + #[case(&[0x03, 0x00, 0x00, 0x00], Some(0x3000000))] + #[case(&[0x03, 0xff, 0xff, 0xff], Some(0x3ffffff))] + fn test_fits_in_small_atom(#[case] buf: &[u8], #[case] expected: Option) { + assert_eq!(fits_in_small_atom(buf), expected); + } + #[rstest] // 0 is encoded as an empty string - let num = number_from_u8(bytes); - assert_eq!(format!("{}", num), text); - let ptr = a.new_number(num).unwrap(); - assert_eq!(a.atom(ptr).as_ref(), buf); + #[case(&[0], "0", &[])] + #[case(&[1], "1", &[1])] + // leading zeroes are redundant + #[case(&[0,0,0,1], "1", &[1])] + #[case(&[0,0,0x80], "128", &[0, 0x80])] + // A leading zero is necessary to encode a positive number with the + // penultimate byte's most significant bit set + #[case(&[0,0xff], "255", &[0, 0xff])] + #[case(&[0x7f,0xff], "32767", &[0x7f, 0xff])] + // the first byte is redundant, it's still -1 + #[case(&[0xff,0xff], "-1", &[0xff])] + #[case(&[0xff], "-1", &[0xff])] + #[case(&[0,0,0x80,0], "32768", &[0,0x80,0])] + #[case(&[0,0,0x40,0], "16384", &[0x40,0])] + fn test_number_to_atom(#[case] bytes: &[u8], #[case] text: &str, #[case] buf: &[u8]) { + let mut a = Allocator::new(); + + // 0 is encoded as an empty string + let num = number_from_u8(bytes); + assert_eq!(format!("{}", num), text); + let ptr = a.new_number(num).unwrap(); + assert_eq!(a.atom(ptr).as_ref(), buf); + } } diff --git a/src/more_ops.rs b/src/more_ops.rs index ebe866ec..7e72a6e4 100644 --- a/src/more_ops.rs +++ b/src/more_ops.rs @@ -982,43 +982,47 @@ pub fn op_modpow(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response } #[cfg(test)] -fn test_sha256_atom(buf: &[u8]) { - let mut a = Allocator::new(); - let mut args = a.nil(); - let v = a.new_atom(buf).unwrap(); - args = a.new_pair(v, args).unwrap(); - let v = a.new_small_number(1).unwrap(); - args = a.new_pair(v, args).unwrap(); - - let cost = SHA256_BASE_COST - + (2 * SHA256_COST_PER_ARG) - + ((1 + buf.len()) as Cost * SHA256_COST_PER_BYTE) - + 32 * MALLOC_COST_PER_BYTE; - let Reduction(actual_cost, result) = op_sha256(&mut a, args, cost).unwrap(); +mod tests { + use super::*; + + fn test_sha256_atom(buf: &[u8]) { + let mut a = Allocator::new(); + let mut args = a.nil(); + let v = a.new_atom(buf).unwrap(); + args = a.new_pair(v, args).unwrap(); + let v = a.new_small_number(1).unwrap(); + args = a.new_pair(v, args).unwrap(); + + let cost = SHA256_BASE_COST + + (2 * SHA256_COST_PER_ARG) + + ((1 + buf.len()) as Cost * SHA256_COST_PER_BYTE) + + 32 * MALLOC_COST_PER_BYTE; + let Reduction(actual_cost, result) = op_sha256(&mut a, args, cost).unwrap(); + + let mut hasher = Sha256::new(); + hasher.update([1_u8]); + if !buf.is_empty() { + hasher.update(buf); + } - let mut hasher = Sha256::new(); - hasher.update([1_u8]); - if !buf.is_empty() { - hasher.update(buf); + println!("buf: {buf:?}"); + assert_eq!(a.atom(result).as_ref(), hasher.finalize().as_slice()); + assert_eq!(actual_cost, cost); } - println!("buf: {buf:?}"); - assert_eq!(a.atom(result).as_ref(), hasher.finalize().as_slice()); - assert_eq!(actual_cost, cost); -} - -#[test] -fn sha256_small_values() { - test_sha256_atom(&[]); - for val in 0..255 { - test_sha256_atom(&[val]); - } + #[test] + fn sha256_small_values() { + test_sha256_atom(&[]); + for val in 0..255 { + test_sha256_atom(&[val]); + } - for val in 0..255 { - test_sha256_atom(&[0, val]); - } + for val in 0..255 { + test_sha256_atom(&[0, val]); + } - for val in 0..255 { - test_sha256_atom(&[0xff, val]); + for val in 0..255 { + test_sha256_atom(&[0xff, val]); + } } } diff --git a/src/number.rs b/src/number.rs index 8b86c8da..0220ac5d 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,4 +1,5 @@ use num_bigint::BigInt; + pub type Number = BigInt; // This low-level conversion function is meant to be used by the Allocator, for @@ -14,271 +15,271 @@ pub fn number_from_u8(v: &[u8]) -> Number { } #[cfg(test)] -use num_bigint::{BigUint, Sign}; - -#[cfg(test)] -fn roundtrip_bytes(b: &[u8]) { - let negative = !b.is_empty() && (b[0] & 0x80) != 0; - let zero = b.is_empty() || (b.len() == 1 && b[0] == 0); - - { - let num = Number::from_signed_bytes_be(b); - - if negative { - assert!(num.sign() == Sign::Minus); - } else if zero { - assert!(num.sign() == Sign::NoSign); - } else { - assert!(num.sign() == Sign::Plus); +mod tests { + use num_bigint::{BigUint, Sign}; + + use super::*; + + fn roundtrip_bytes(b: &[u8]) { + let negative = !b.is_empty() && (b[0] & 0x80) != 0; + let zero = b.is_empty() || (b.len() == 1 && b[0] == 0); + + { + let num = Number::from_signed_bytes_be(b); + + if negative { + assert!(num.sign() == Sign::Minus); + } else if zero { + assert!(num.sign() == Sign::NoSign); + } else { + assert!(num.sign() == Sign::Plus); + } + + let round_trip = num.to_signed_bytes_be(); + // num-bigin produces a single 0 byte for the value 0. We expect an + // empty array + let round_trip = if round_trip == [0] { + &round_trip[1..] + } else { + &round_trip + }; + + assert_eq!(round_trip, b); + + // test to_bytes_le() + let (sign, mut buf_le) = num.to_bytes_le(); + + // there's a special case for empty input buffers, which will result in + // a single 0 byte here + if b.is_empty() { + assert_eq!(buf_le, &[0]); + buf_le.remove(0); + } + assert!(sign == num.sign()); + + // the buffer we get from to_bytes_le() is unsigned (since the sign is + // returned separately). This means it doesn't ever need to prepend a 0 + // byte when the MSB is set. When we're comparing this against the input + // buffer, we need to add such 0 byte to buf_le to make them compare + // equal. + // the 0 prefix has to be added to the end though, since it's little + // endian + if !buf_le.is_empty() && (buf_le.last().unwrap() & 0x80) != 0 { + buf_le.push(0); + } + + if sign != Sign::Minus { + assert!(buf_le.iter().eq(b.iter().rev())); + } else { + let negated = -num; + let magnitude = negated.to_signed_bytes_be(); + assert!(buf_le.iter().eq(magnitude.iter().rev())); + } } - let round_trip = num.to_signed_bytes_be(); - // num-bigin produces a single 0 byte for the value 0. We expect an - // empty array - let round_trip = if round_trip == [0] { - &round_trip[1..] - } else { - &round_trip - }; - - assert_eq!(round_trip, b); - - // test to_bytes_le() - let (sign, mut buf_le) = num.to_bytes_le(); - - // there's a special case for empty input buffers, which will result in - // a single 0 byte here - if b.is_empty() { - assert_eq!(buf_le, &[0]); - buf_le.remove(0); - } - assert!(sign == num.sign()); - - // the buffer we get from to_bytes_le() is unsigned (since the sign is - // returned separately). This means it doesn't ever need to prepend a 0 - // byte when the MSB is set. When we're comparing this against the input - // buffer, we need to add such 0 byte to buf_le to make them compare - // equal. - // the 0 prefix has to be added to the end though, since it's little - // endian - if !buf_le.is_empty() && (buf_le.last().unwrap() & 0x80) != 0 { - buf_le.push(0); + // test parsing unsigned bytes + { + let unsigned_num: Number = BigUint::from_bytes_be(b).into(); + assert!(unsigned_num.sign() != Sign::Minus); + let unsigned_round_trip = unsigned_num.to_signed_bytes_be(); + let unsigned_round_trip = if unsigned_round_trip == [0] { + &unsigned_round_trip[1..] + } else { + &unsigned_round_trip + }; + if !b.is_empty() && (b[0] & 0x80) != 0 { + // we expect a new leading zero here, to keep the value positive + assert!(unsigned_round_trip[0] == 0); + assert_eq!(&unsigned_round_trip[1..], b); + } else { + assert_eq!(unsigned_round_trip, b); + } } + } + + #[test] + fn test_number_round_trip_bytes() { + roundtrip_bytes(&[]); - if sign != Sign::Minus { - assert!(buf_le.iter().eq(b.iter().rev())); - } else { - let negated = -num; - let magnitude = negated.to_signed_bytes_be(); - assert!(buf_le.iter().eq(magnitude.iter().rev())); + for i in 1..=255 { + roundtrip_bytes(&[i]); } - } - // test parsing unsigned bytes - { - let unsigned_num: Number = BigUint::from_bytes_be(b).into(); - assert!(unsigned_num.sign() != Sign::Minus); - let unsigned_round_trip = unsigned_num.to_signed_bytes_be(); - let unsigned_round_trip = if unsigned_round_trip == [0] { - &unsigned_round_trip[1..] - } else { - &unsigned_round_trip - }; - if !b.is_empty() && (b[0] & 0x80) != 0 { - // we expect a new leading zero here, to keep the value positive - assert!(unsigned_round_trip[0] == 0); - assert_eq!(&unsigned_round_trip[1..], b); - } else { - assert_eq!(unsigned_round_trip, b); + for i in 0..=127 { + roundtrip_bytes(&[0xff, i]); } - } -} -#[test] -fn test_number_round_trip_bytes() { - roundtrip_bytes(&[]); + for i in 128..=255 { + roundtrip_bytes(&[0, i]); + } - for i in 1..=255 { - roundtrip_bytes(&[i]); - } + for i in 0..=127 { + roundtrip_bytes(&[ + 0xff, i, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ]); + } - for i in 0..=127 { - roundtrip_bytes(&[0xff, i]); - } + for i in 128..=255 { + roundtrip_bytes(&[ + 0, i, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ]); + } - for i in 128..=255 { - roundtrip_bytes(&[0, i]); - } + for i in 0..=127 { + roundtrip_bytes(&[0xff, i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + } - for i in 0..=127 { - roundtrip_bytes(&[ - 0xff, i, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - ]); + for i in 128..=255 { + roundtrip_bytes(&[0, i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + } } - for i in 128..=255 { - roundtrip_bytes(&[ - 0, i, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - ]); - } + fn roundtrip_u64(v: u64) { + let num: Number = v.into(); + assert!(num.sign() != Sign::Minus); - for i in 0..=127 { - roundtrip_bytes(&[0xff, i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - } + assert!(num.bits() <= 64); - for i in 128..=255 { - roundtrip_bytes(&[0, i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let round_trip: u64 = TryFrom::try_from(num).unwrap(); + assert_eq!(round_trip, v); } -} -#[cfg(test)] -fn roundtrip_u64(v: u64) { - let num: Number = v.into(); - assert!(num.sign() != Sign::Minus); - - assert!(num.bits() <= 64); + #[test] + fn test_round_trip_u64() { + for v in 0..=0x100 { + roundtrip_u64(v); + } - let round_trip: u64 = TryFrom::try_from(num).unwrap(); - assert_eq!(round_trip, v); -} + for v in 0x7ffe..=0x8001 { + roundtrip_u64(v); + } -#[test] -fn test_round_trip_u64() { - for v in 0..=0x100 { - roundtrip_u64(v); - } + for v in 0xfffe..=0x10000 { + roundtrip_u64(v); + } - for v in 0x7ffe..=0x8001 { - roundtrip_u64(v); - } + for v in 0x7ffffffe..=0x80000001 { + roundtrip_u64(v); + } + for v in 0xfffffffe..=0x100000000 { + roundtrip_u64(v); + } - for v in 0xfffe..=0x10000 { - roundtrip_u64(v); - } + for v in 0x7ffffffffffffffe..=0x8000000000000001 { + roundtrip_u64(v); + } - for v in 0x7ffffffe..=0x80000001 { - roundtrip_u64(v); - } - for v in 0xfffffffe..=0x100000000 { - roundtrip_u64(v); + for v in 0xfffffffffffffffe..=0xffffffffffffffff { + roundtrip_u64(v); + } } - for v in 0x7ffffffffffffffe..=0x8000000000000001 { - roundtrip_u64(v); - } + fn roundtrip_i64(v: i64) { + use std::cmp::Ordering; - for v in 0xfffffffffffffffe..=0xffffffffffffffff { - roundtrip_u64(v); - } -} + let num: Number = v.into(); -#[cfg(test)] -fn roundtrip_i64(v: i64) { - use std::cmp::Ordering; + match v.cmp(&0) { + Ordering::Equal => assert!(num.sign() == Sign::NoSign), + Ordering::Less => assert!(num.sign() == Sign::Minus), + Ordering::Greater => assert!(num.sign() == Sign::Plus), + } - let num: Number = v.into(); + assert!(num.bits() <= 64); - match v.cmp(&0) { - Ordering::Equal => assert!(num.sign() == Sign::NoSign), - Ordering::Less => assert!(num.sign() == Sign::Minus), - Ordering::Greater => assert!(num.sign() == Sign::Plus), + let round_trip: i64 = TryFrom::try_from(num).unwrap(); + assert_eq!(round_trip, v); } - assert!(num.bits() <= 64); + #[test] + fn test_round_trip_i64() { + for v in -0x100..=0x100 { + roundtrip_i64(v); + } - let round_trip: i64 = TryFrom::try_from(num).unwrap(); - assert_eq!(round_trip, v); -} + for v in 0x7ffe..=0x8001 { + roundtrip_i64(v); + } -#[test] -fn test_round_trip_i64() { - for v in -0x100..=0x100 { - roundtrip_i64(v); - } + for v in -0x8001..-0x7ffe { + roundtrip_i64(v); + } - for v in 0x7ffe..=0x8001 { - roundtrip_i64(v); - } + for v in 0xfffe..=0x10000 { + roundtrip_i64(v); + } - for v in -0x8001..-0x7ffe { - roundtrip_i64(v); - } + for v in -0x10000..-0xfffe { + roundtrip_i64(v); + } - for v in 0xfffe..=0x10000 { - roundtrip_i64(v); - } + for v in 0x7ffffffe..=0x80000001 { + roundtrip_i64(v); + } - for v in -0x10000..-0xfffe { - roundtrip_i64(v); - } + for v in -0x80000001..-0x7ffffffe { + roundtrip_i64(v); + } - for v in 0x7ffffffe..=0x80000001 { - roundtrip_i64(v); - } + for v in 0xfffffffe..=0x100000000 { + roundtrip_i64(v); + } - for v in -0x80000001..-0x7ffffffe { - roundtrip_i64(v); - } + for v in -0x100000000..-0xfffffffe { + roundtrip_i64(v); + } - for v in 0xfffffffe..=0x100000000 { - roundtrip_i64(v); - } + for v in 0x7ffffffffffffffe..=0x7fffffffffffffff { + roundtrip_i64(v); + } - for v in -0x100000000..-0xfffffffe { - roundtrip_i64(v); + for v in -0x8000000000000000..-0x7ffffffffffffffe { + roundtrip_i64(v); + } } - for v in 0x7ffffffffffffffe..=0x7fffffffffffffff { - roundtrip_i64(v); + fn bits(b: &[u8]) -> u64 { + Number::from_signed_bytes_be(b).bits() } - for v in -0x8000000000000000..-0x7ffffffffffffffe { - roundtrip_i64(v); + #[test] + fn test_bits() { + assert_eq!(bits(&[]), 0); + assert_eq!(bits(&[0]), 0); + assert_eq!(bits(&[0b01111111]), 7); + assert_eq!(bits(&[0b00111111]), 6); + assert_eq!(bits(&[0b00011111]), 5); + assert_eq!(bits(&[0b00001111]), 4); + assert_eq!(bits(&[0b00000111]), 3); + assert_eq!(bits(&[0b00000011]), 2); + assert_eq!(bits(&[0b00000001]), 1); + assert_eq!(bits(&[0b00000000]), 0); + + assert_eq!(bits(&[0b01111111, 0xff]), 15); + assert_eq!(bits(&[0b00111111, 0xff]), 14); + assert_eq!(bits(&[0b00011111, 0xff]), 13); + assert_eq!(bits(&[0b00001111, 0xff]), 12); + assert_eq!(bits(&[0b00000111, 0xff]), 11); + assert_eq!(bits(&[0b00000011, 0xff]), 10); + assert_eq!(bits(&[0b00000001, 0xff]), 9); + assert_eq!(bits(&[0b00000000, 0xff]), 8); + + assert_eq!(bits(&[0b11111111]), 1); + assert_eq!(bits(&[0b11111110]), 2); + assert_eq!(bits(&[0b11111100]), 3); + assert_eq!(bits(&[0b11111000]), 4); + assert_eq!(bits(&[0b11110000]), 5); + assert_eq!(bits(&[0b11100000]), 6); + assert_eq!(bits(&[0b11000000]), 7); + assert_eq!(bits(&[0b10000000]), 8); + + assert_eq!(bits(&[0b11111111, 0]), 9); + assert_eq!(bits(&[0b11111110, 0]), 10); + assert_eq!(bits(&[0b11111100, 0]), 11); + assert_eq!(bits(&[0b11111000, 0]), 12); + assert_eq!(bits(&[0b11110000, 0]), 13); + assert_eq!(bits(&[0b11100000, 0]), 14); + assert_eq!(bits(&[0b11000000, 0]), 15); + assert_eq!(bits(&[0b10000000, 0]), 16); } } - -#[cfg(test)] -fn bits(b: &[u8]) -> u64 { - Number::from_signed_bytes_be(b).bits() -} - -#[test] -fn test_bits() { - assert_eq!(bits(&[]), 0); - assert_eq!(bits(&[0]), 0); - assert_eq!(bits(&[0b01111111]), 7); - assert_eq!(bits(&[0b00111111]), 6); - assert_eq!(bits(&[0b00011111]), 5); - assert_eq!(bits(&[0b00001111]), 4); - assert_eq!(bits(&[0b00000111]), 3); - assert_eq!(bits(&[0b00000011]), 2); - assert_eq!(bits(&[0b00000001]), 1); - assert_eq!(bits(&[0b00000000]), 0); - - assert_eq!(bits(&[0b01111111, 0xff]), 15); - assert_eq!(bits(&[0b00111111, 0xff]), 14); - assert_eq!(bits(&[0b00011111, 0xff]), 13); - assert_eq!(bits(&[0b00001111, 0xff]), 12); - assert_eq!(bits(&[0b00000111, 0xff]), 11); - assert_eq!(bits(&[0b00000011, 0xff]), 10); - assert_eq!(bits(&[0b00000001, 0xff]), 9); - assert_eq!(bits(&[0b00000000, 0xff]), 8); - - assert_eq!(bits(&[0b11111111]), 1); - assert_eq!(bits(&[0b11111110]), 2); - assert_eq!(bits(&[0b11111100]), 3); - assert_eq!(bits(&[0b11111000]), 4); - assert_eq!(bits(&[0b11110000]), 5); - assert_eq!(bits(&[0b11100000]), 6); - assert_eq!(bits(&[0b11000000]), 7); - assert_eq!(bits(&[0b10000000]), 8); - - assert_eq!(bits(&[0b11111111, 0]), 9); - assert_eq!(bits(&[0b11111110, 0]), 10); - assert_eq!(bits(&[0b11111100, 0]), 11); - assert_eq!(bits(&[0b11111000, 0]), 12); - assert_eq!(bits(&[0b11110000, 0]), 13); - assert_eq!(bits(&[0b11100000, 0]), 14); - assert_eq!(bits(&[0b11000000, 0]), 15); - assert_eq!(bits(&[0b10000000, 0]), 16); -} diff --git a/src/op_utils.rs b/src/op_utils.rs index 464075a0..9e207d57 100644 --- a/src/op_utils.rs +++ b/src/op_utils.rs @@ -48,209 +48,6 @@ pub fn match_args(a: &Allocator, args: NodePtr) -> Option<[NodeP } } -#[test] -fn test_get_args() { - let mut a = Allocator::new(); - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - let a2 = a.new_number(0.into()).unwrap(); - let a3 = a.new_atom(&[]).unwrap(); - let args0 = a.nil(); - let args1 = a.new_pair(a3, args0).unwrap(); - let args2 = a.new_pair(a2, args1).unwrap(); - let args3 = a.new_pair(a1, args2).unwrap(); - let args4 = a.new_pair(a0, args3).unwrap(); - - assert_eq!(get_args::<4>(&a, args4, "test").unwrap(), [a0, a1, a2, a3]); - - let r = get_args::<3>(&a, args4, "test").unwrap_err(); - assert_eq!(r.0, args4); - assert_eq!(r.1, "test takes exactly 3 arguments"); - - let r = get_args::<5>(&a, args4, "test").unwrap_err(); - assert_eq!(r.0, args4); - assert_eq!(r.1, "test takes exactly 5 arguments"); - - let r = get_args::<4>(&a, args3, "test").unwrap_err(); - assert_eq!(r.0, args3); - assert_eq!(r.1, "test takes exactly 4 arguments"); - - let r = get_args::<4>(&a, args2, "test").unwrap_err(); - assert_eq!(r.0, args2); - assert_eq!(r.1, "test takes exactly 4 arguments"); - - let r = get_args::<1>(&a, args2, "test").unwrap_err(); - assert_eq!(r.0, args2); - assert_eq!(r.1, "test takes exactly 1 argument"); -} - -pub fn get_varargs( - a: &Allocator, - args: NodePtr, - name: &str, -) -> Result<([NodePtr; N], usize), EvalErr> { - let mut next = args; - let mut counter = 0; - let mut ret = [NodePtr::NIL; N]; - - while let Some((first, rest)) = a.next(next) { - next = rest; - if counter == N { - return err( - args, - &format!( - "{name} takes no more than {N} argument{}", - if N == 1 { "" } else { "s" } - ), - ); - } - ret[counter] = first; - counter += 1; - } - - Ok((ret, counter)) -} - -#[test] -fn test_get_varargs() { - let mut a = Allocator::new(); - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - let a2 = a.new_number(0.into()).unwrap(); - let a3 = a.new_atom(&[]).unwrap(); - let args0 = a.nil(); - let args1 = a.new_pair(a3, args0).unwrap(); - let args2 = a.new_pair(a2, args1).unwrap(); - let args3 = a.new_pair(a1, args2).unwrap(); - let args4 = a.new_pair(a0, args3).unwrap(); - - // happy path - assert_eq!( - get_varargs::<4>(&a, args4, "test").unwrap(), - ([a0, a1, a2, a3], 4) - ); - assert_eq!( - get_varargs::<4>(&a, args3, "test").unwrap(), - ([a1, a2, a3, NodePtr::NIL], 3) - ); - assert_eq!( - get_varargs::<4>(&a, args2, "test").unwrap(), - ([a2, a3, NodePtr::NIL, NodePtr::NIL], 2) - ); - assert_eq!( - get_varargs::<4>(&a, args1, "test").unwrap(), - ([a3, NodePtr::NIL, NodePtr::NIL, NodePtr::NIL], 1) - ); - assert_eq!( - get_varargs::<4>(&a, args0, "test").unwrap(), - ([NodePtr::NIL; 4], 0) - ); - - let r = get_varargs::<3>(&a, args4, "test").unwrap_err(); - assert_eq!(r.0, args4); - assert_eq!(r.1, "test takes no more than 3 arguments"); - - let r = get_varargs::<1>(&a, args4, "test").unwrap_err(); - assert_eq!(r.0, args4); - assert_eq!(r.1, "test takes no more than 1 argument"); -} - -pub fn nilp(a: &Allocator, n: NodePtr) -> bool { - match a.sexp(n) { - SExp::Atom => a.atom_len(n) == 0, - _ => false, - } -} - -#[test] -fn test_nilp() { - let mut a = Allocator::new(); - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - let a3 = a.new_number(0.into()).unwrap(); - let a4 = a.new_atom(&[]).unwrap(); - let a5 = a.nil(); - let pair = a.new_pair(a0, a1).unwrap(); - assert!(!nilp(&a, pair)); - assert!(!nilp(&a, a0)); - assert!(!nilp(&a, a1)); - assert!(nilp(&a, a3)); - assert!(nilp(&a, a4)); - assert!(nilp(&a, a5)); -} - -pub fn first(a: &Allocator, n: NodePtr) -> Result { - match a.sexp(n) { - SExp::Pair(first, _) => Ok(first), - _ => err(n, "first of non-cons"), - } -} - -#[test] -fn test_first() { - let mut a = Allocator::new(); - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - let pair = a.new_pair(a0, a1).unwrap(); - assert_eq!(first(&a, pair).unwrap(), a0); - - let r = first(&a, a0).unwrap_err(); - assert_eq!(r.0, a0); - assert_eq!(r.1, "first of non-cons"); -} - -pub fn rest(a: &Allocator, n: NodePtr) -> Result { - match a.sexp(n) { - SExp::Pair(_, rest) => Ok(rest), - _ => err(n, "rest of non-cons"), - } -} - -#[test] -fn test_rest() { - let mut a = Allocator::new(); - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - let pair = a.new_pair(a0, a1).unwrap(); - assert_eq!(rest(&a, pair).unwrap(), a1); - - let r = rest(&a, a0).unwrap_err(); - assert_eq!(r.0, a0); - assert_eq!(r.1, "rest of non-cons"); -} - -pub fn int_atom(a: &Allocator, args: NodePtr, op_name: &str) -> Result<(Number, usize), EvalErr> { - match a.sexp(args) { - SExp::Atom => Ok((a.number(args), a.atom_len(args))), - _ => err(args, &format!("{op_name} requires int args")), - } -} - -#[cfg(test)] -#[rstest] -#[case(0.into(), (0.into(), 0))] -#[case(1.into(), (1.into(), 1))] -#[case(42.into(), (42.into(), 1))] -#[case(1337.into(), (1337.into(), 2))] -#[case(0x5fffff.into(), (0x5fffff.into(), 3))] -#[case(0xffffff.into(), (0xffffff.into(), 4))] -fn test_int_atom(#[case] value: Number, #[case] expected: (Number, usize)) { - let mut a = Allocator::new(); - let a0 = a.new_number(value).unwrap(); - assert_eq!(int_atom(&a, a0, "test").unwrap(), expected); -} - -#[test] -fn test_int_atom_failure() { - let mut a = Allocator::new(); - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - let pair = a.new_pair(a0, a1).unwrap(); - let r = int_atom(&a, pair, "test").unwrap_err(); - assert_eq!(r.0, pair); - assert_eq!(r.1, "test requires int args"); -} - pub fn atom_len(a: &Allocator, args: NodePtr, op_name: &str) -> Result { match a.sexp(args) { SExp::Atom => Ok(a.atom_len(args)), @@ -258,22 +55,6 @@ pub fn atom_len(a: &Allocator, args: NodePtr, op_name: &str) -> Result( a: &Allocator, args: NodePtr, @@ -311,100 +92,6 @@ pub fn uint_atom( } } -#[cfg(test)] -use rstest::rstest; - -// u32, 4 bytes -#[cfg(test)] -#[rstest] -#[case(&[0], 0)] -#[case(&[0,0,0,1], 1)] -#[case(&[0,0xff,0xff,0xff,0xff], 0xffffffff)] -#[case(&[0,0,0,0,0,0xff,0xff,0xff,0xff], 0xffffffff)] -#[case(&[0x7f,0xff], 0x7fff)] -#[case(&[0x7f,0xff, 0xff], 0x7fffff)] -#[case(&[0x7f,0xff,0xff, 0xff], 0x7fffffff)] -#[case(&[0x01,0x02,0x03, 0x04], 0x1020304)] -#[case(&[] as &[u8], 0)] -fn test_uint_atom_4_success(#[case] buf: &[u8], #[case] expected: u64) { - use crate::allocator::Allocator; - let mut a = Allocator::new(); - let n = a.new_atom(buf).unwrap(); - assert!(uint_atom::<4>(&a, n, "test") == Ok(expected)); -} - -// u32, 4 bytes -#[cfg(test)] -#[rstest] -#[case(&[0xff,0xff,0xff,0xff], "test requires positive int arg")] -#[case(&[0xff], "test requires positive int arg")] -#[case(&[0x80], "test requires positive int arg")] -#[case(&[0x80,0,0,0], "test requires positive int arg")] -#[case(&[1, 0xff,0xff,0xff,0xff], "test requires u32 arg")] -fn test_uint_atom_4_failure(#[case] buf: &[u8], #[case] expected: &str) { - use crate::allocator::Allocator; - let mut a = Allocator::new(); - let n = a.new_atom(buf).unwrap(); - assert!(uint_atom::<4>(&a, n, "test") == err(n, expected)); -} - -#[test] -fn test_uint_atom_4_pair() { - use crate::allocator::Allocator; - let mut a = Allocator::new(); - let n = a.new_atom(&[0, 0]).unwrap(); - let p = a.new_pair(n, n).unwrap(); - assert!(uint_atom::<4>(&a, p, "test") == err(p, "test requires int arg")); -} - -// u64, 8 bytes -#[cfg(test)] -#[rstest] -#[case(&[0], 0)] -#[case(&[0,0,0,1], 1)] -#[case(&[0,0xff,0xff,0xff,0xff], 0xffffffff)] -#[case(&[0,0,0,0,0xff,0xff,0xff,0xff], 0xffffffff)] -#[case(&[0x7f, 0xff], 0x7fff)] -#[case(&[0x7f, 0xff, 0xff], 0x7fffff)] -#[case(&[0x7f, 0xff,0xff, 0xff], 0x7fffffff)] -#[case(&[0x7f, 0xff,0xff, 0xff, 0xff], 0x7fffffffff)] -#[case(&[0x7f, 0xff,0xff, 0xff, 0xff, 0xff], 0x7fffffffffff)] -#[case(&[0x7f, 0xff,0xff, 0xff, 0xff, 0xff, 0xff], 0x7fffffffffffff)] -#[case(&[0x7f, 0xff,0xff, 0xff, 0xff, 0xff, 0xff, 0xff], 0x7fffffffffffffff)] -#[case(&[0x01, 0x02,0x03, 0x04, 0x05, 0x06, 0x07, 0x08 ], 0x102030405060708)] -#[case(&[] as &[u8], 0)] -fn test_uint_atom_8_success(#[case] buf: &[u8], #[case] expected: u64) { - use crate::allocator::Allocator; - let mut a = Allocator::new(); - let n = a.new_atom(buf).unwrap(); - assert!(uint_atom::<8>(&a, n, "test") == Ok(expected)); -} - -// u64, 8 bytes -#[cfg(test)] -#[rstest] -#[case(&[0xff,0xff,0xff,0xff], "test requires positive int arg")] -#[case(&[0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff], "test requires positive int arg")] -#[case(&[0xff], "test requires positive int arg")] -#[case(&[0x80], "test requires positive int arg")] -#[case(&[0x80,0,0,0], "test requires positive int arg")] -#[case(&[1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff], "test requires u64 arg")] -fn test_uint_atom_8_failure(#[case] buf: &[u8], #[case] expected: &str) { - use crate::allocator::Allocator; - let mut a = Allocator::new(); - let n = a.new_atom(buf).unwrap(); - assert!(uint_atom::<8>(&a, n, "test") == err(n, expected)); -} - -#[test] -fn test_uint_atom_8_pair() { - use crate::allocator::Allocator; - let mut a = Allocator::new(); - let n = a.new_atom(&[0, 0]).unwrap(); - let p = a.new_pair(n, n).unwrap(); - assert!(uint_atom::<8>(&a, p, "test") == err(p, "test requires int arg")); -} - pub fn atom<'a>(a: &'a Allocator, n: NodePtr, op_name: &str) -> Result, EvalErr> { if n.is_pair() { return err(n, &format!("{op_name} on list")); @@ -412,6 +99,20 @@ pub fn atom<'a>(a: &'a Allocator, n: NodePtr, op_name: &str) -> Result, Ok(a.atom(n)) } +pub fn i32_atom(a: &Allocator, args: NodePtr, op_name: &str) -> Result { + match a.node(args) { + NodeVisitor::Buffer(buf) => match i32_from_u8(buf) { + Some(v) => Ok(v), + _ => err( + args, + &format!("{op_name} requires int32 args (with no leading zeros)"), + ), + }, + NodeVisitor::U32(val) => Ok(val as i32), + NodeVisitor::Pair(_, _) => err(args, &format!("{op_name} requires int32 args")), + } +} + fn u32_from_u8_impl(buf: &[u8], signed: bool) -> Option { if buf.is_empty() { return Some(0); @@ -435,63 +136,10 @@ pub fn u32_from_u8(buf: &[u8]) -> Option { u32_from_u8_impl(buf, false) } -#[test] -fn test_u32_from_u8() { - assert_eq!(u32_from_u8(&[]), Some(0)); - assert_eq!(u32_from_u8(&[0xcc]), Some(0xcc)); - assert_eq!(u32_from_u8(&[0xcc, 0x55]), Some(0xcc55)); - assert_eq!(u32_from_u8(&[0xcc, 0x55, 0x88]), Some(0xcc5588)); - assert_eq!(u32_from_u8(&[0xcc, 0x55, 0x88, 0xf3]), Some(0xcc5588f3)); - - assert_eq!(u32_from_u8(&[0xff]), Some(0xff)); - assert_eq!(u32_from_u8(&[0xff, 0xff]), Some(0xffff)); - assert_eq!(u32_from_u8(&[0xff, 0xff, 0xff]), Some(0xffffff)); - assert_eq!(u32_from_u8(&[0xff, 0xff, 0xff, 0xff]), Some(0xffffffff)); - - // leading zeros are not stripped, and not allowed beyond 4 bytes - assert_eq!(u32_from_u8(&[0x00]), Some(0)); - assert_eq!(u32_from_u8(&[0x00, 0x00]), Some(0)); - assert_eq!(u32_from_u8(&[0x00, 0xcc, 0x55, 0x88]), Some(0xcc5588)); - assert_eq!(u32_from_u8(&[0x00, 0x00, 0xcc, 0x55, 0x88]), None); - assert_eq!(u32_from_u8(&[0x00, 0xcc, 0x55, 0x88, 0xf3]), None); - - // overflow, too many bytes - assert_eq!(u32_from_u8(&[0x01, 0xcc, 0x55, 0x88, 0xf3]), None); - assert_eq!(u32_from_u8(&[0x01, 0x00, 0x00, 0x00, 0x00]), None); - assert_eq!(u32_from_u8(&[0x7d, 0xcc, 0x55, 0x88, 0xf3]), None); -} - pub fn i32_from_u8(buf: &[u8]) -> Option { u32_from_u8_impl(buf, true).map(|v| v as i32) } -#[test] -fn test_i32_from_u8() { - assert_eq!(i32_from_u8(&[]), Some(0)); - assert_eq!(i32_from_u8(&[0xcc]), Some(-52)); - assert_eq!(i32_from_u8(&[0xcc, 0x55]), Some(-13227)); - assert_eq!(i32_from_u8(&[0xcc, 0x55, 0x88]), Some(-3385976)); - assert_eq!(i32_from_u8(&[0xcc, 0x55, 0x88, 0xf3]), Some(-866809613)); - - assert_eq!(i32_from_u8(&[0xff]), Some(-1)); - assert_eq!(i32_from_u8(&[0xff, 0xff]), Some(-1)); - assert_eq!(i32_from_u8(&[0xff, 0xff, 0xff]), Some(-1)); - assert_eq!(i32_from_u8(&[0xff, 0xff, 0xff, 0xff]), Some(-1)); - - // leading zeros are not stripped, and not allowed beyond 4 bytes - assert_eq!(i32_from_u8(&[0x00]), Some(0)); - assert_eq!(i32_from_u8(&[0x00, 0x00]), Some(0)); - assert_eq!(i32_from_u8(&[0x00, 0xcc, 0x55, 0x88]), Some(0xcc5588)); - assert_eq!(i32_from_u8(&[0x00, 0x00, 0xcc, 0x55, 0x88]), None); - assert_eq!(i32_from_u8(&[0x00, 0xcc, 0x55, 0x88, 0xf3]), None); - - // overflow, it doesn't really matter whether the bytes are 0 or not, any - // atom larger than 4 bytes is rejected - assert_eq!(i32_from_u8(&[0x01, 0xcc, 0x55, 0x88, 0xf3]), None); - assert_eq!(i32_from_u8(&[0x01, 0x00, 0x00, 0x00, 0x00]), None); - assert_eq!(i32_from_u8(&[0x7d, 0xcc, 0x55, 0x88, 0xf3]), None); -} - pub fn u64_from_bytes(buf: &[u8]) -> u64 { if buf.is_empty() { return 0; @@ -505,72 +153,6 @@ pub fn u64_from_bytes(buf: &[u8]) -> u64 { ret } -#[test] -fn test_u64_from_bytes() { - assert_eq!(u64_from_bytes(&[]), 0); - assert_eq!(u64_from_bytes(&[0xcc]), 0xcc); - assert_eq!(u64_from_bytes(&[0xcc, 0x55]), 0xcc55); - assert_eq!(u64_from_bytes(&[0xcc, 0x55, 0x88]), 0xcc5588); - assert_eq!(u64_from_bytes(&[0xcc, 0x55, 0x88, 0xf3]), 0xcc5588f3); - - assert_eq!(u64_from_bytes(&[0xff]), 0xff); - assert_eq!(u64_from_bytes(&[0xff, 0xff]), 0xffff); - assert_eq!(u64_from_bytes(&[0xff, 0xff, 0xff]), 0xffffff); - assert_eq!(u64_from_bytes(&[0xff, 0xff, 0xff, 0xff]), 0xffffffff); - - assert_eq!(u64_from_bytes(&[0x00]), 0); - assert_eq!(u64_from_bytes(&[0x00, 0x00]), 0); - assert_eq!(u64_from_bytes(&[0x00, 0xcc, 0x55, 0x88]), 0xcc5588); - assert_eq!(u64_from_bytes(&[0x00, 0x00, 0xcc, 0x55, 0x88]), 0xcc5588); - assert_eq!(u64_from_bytes(&[0x00, 0xcc, 0x55, 0x88, 0xf3]), 0xcc5588f3); - - assert_eq!( - u64_from_bytes(&[0xcc, 0x55, 0x88, 0xf3, 0xcc, 0x55, 0x88, 0xf3]), - 0xcc5588f3cc5588f3 - ); -} - -pub fn i32_atom(a: &Allocator, args: NodePtr, op_name: &str) -> Result { - match a.node(args) { - NodeVisitor::Buffer(buf) => match i32_from_u8(buf) { - Some(v) => Ok(v), - _ => err( - args, - &format!("{op_name} requires int32 args (with no leading zeros)"), - ), - }, - NodeVisitor::U32(val) => Ok(val as i32), - NodeVisitor::Pair(_, _) => err(args, &format!("{op_name} requires int32 args")), - } -} - -#[test] -fn test_i32_atom() { - let mut a = Allocator::new(); - - let a0 = a.new_number(42.into()).unwrap(); - let a1 = a.new_number(1337.into()).unwrap(); - - let pair = a.new_pair(a0, a1).unwrap(); - - let r = i32_atom(&a, pair, "test").unwrap_err(); - assert_eq!(r.0, pair); - assert_eq!(r.1, "test requires int32 args"); - - assert_eq!(i32_atom(&a, a0, "test").unwrap(), 42); - assert_eq!(i32_atom(&a, a1, "test").unwrap(), 1337); - - let a2 = a.new_number(0x100000000_i64.into()).unwrap(); - let r = i32_atom(&a, a2, "test").unwrap_err(); - assert_eq!(r.0, a2); - assert_eq!(r.1, "test requires int32 args (with no leading zeros)"); - - let a3 = a.new_number((-0xffffffff_i64).into()).unwrap(); - let r = i32_atom(&a, a3, "test").unwrap_err(); - assert_eq!(r.0, a3); - assert_eq!(r.1, "test requires int32 args (with no leading zeros)"); -} - pub fn new_atom_and_cost(a: &mut Allocator, cost: Cost, buf: &[u8]) -> Response { let c = buf.len() as Cost * MALLOC_COST_PER_BYTE; Ok(Reduction(cost + c, a.new_atom(buf)?)) @@ -596,3 +178,420 @@ lazy_static! { n.into() }; } + +pub fn get_varargs( + a: &Allocator, + args: NodePtr, + name: &str, +) -> Result<([NodePtr; N], usize), EvalErr> { + let mut next = args; + let mut counter = 0; + let mut ret = [NodePtr::NIL; N]; + + while let Some((first, rest)) = a.next(next) { + next = rest; + if counter == N { + return err( + args, + &format!( + "{name} takes no more than {N} argument{}", + if N == 1 { "" } else { "s" } + ), + ); + } + ret[counter] = first; + counter += 1; + } + + Ok((ret, counter)) +} + +pub fn nilp(a: &Allocator, n: NodePtr) -> bool { + match a.sexp(n) { + SExp::Atom => a.atom_len(n) == 0, + _ => false, + } +} + +pub fn first(a: &Allocator, n: NodePtr) -> Result { + match a.sexp(n) { + SExp::Pair(first, _) => Ok(first), + _ => err(n, "first of non-cons"), + } +} + +pub fn rest(a: &Allocator, n: NodePtr) -> Result { + match a.sexp(n) { + SExp::Pair(_, rest) => Ok(rest), + _ => err(n, "rest of non-cons"), + } +} + +pub fn int_atom(a: &Allocator, args: NodePtr, op_name: &str) -> Result<(Number, usize), EvalErr> { + match a.sexp(args) { + SExp::Atom => Ok((a.number(args), a.atom_len(args))), + _ => err(args, &format!("{op_name} requires int args")), + } +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + + use super::*; + + #[test] + fn test_get_args() { + let mut a = Allocator::new(); + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let a2 = a.new_number(0.into()).unwrap(); + let a3 = a.new_atom(&[]).unwrap(); + let args0 = a.nil(); + let args1 = a.new_pair(a3, args0).unwrap(); + let args2 = a.new_pair(a2, args1).unwrap(); + let args3 = a.new_pair(a1, args2).unwrap(); + let args4 = a.new_pair(a0, args3).unwrap(); + + assert_eq!(get_args::<4>(&a, args4, "test").unwrap(), [a0, a1, a2, a3]); + + let r = get_args::<3>(&a, args4, "test").unwrap_err(); + assert_eq!(r.0, args4); + assert_eq!(r.1, "test takes exactly 3 arguments"); + + let r = get_args::<5>(&a, args4, "test").unwrap_err(); + assert_eq!(r.0, args4); + assert_eq!(r.1, "test takes exactly 5 arguments"); + + let r = get_args::<4>(&a, args3, "test").unwrap_err(); + assert_eq!(r.0, args3); + assert_eq!(r.1, "test takes exactly 4 arguments"); + + let r = get_args::<4>(&a, args2, "test").unwrap_err(); + assert_eq!(r.0, args2); + assert_eq!(r.1, "test takes exactly 4 arguments"); + + let r = get_args::<1>(&a, args2, "test").unwrap_err(); + assert_eq!(r.0, args2); + assert_eq!(r.1, "test takes exactly 1 argument"); + } + + #[test] + fn test_get_varargs() { + let mut a = Allocator::new(); + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let a2 = a.new_number(0.into()).unwrap(); + let a3 = a.new_atom(&[]).unwrap(); + let args0 = a.nil(); + let args1 = a.new_pair(a3, args0).unwrap(); + let args2 = a.new_pair(a2, args1).unwrap(); + let args3 = a.new_pair(a1, args2).unwrap(); + let args4 = a.new_pair(a0, args3).unwrap(); + + // happy path + assert_eq!( + get_varargs::<4>(&a, args4, "test").unwrap(), + ([a0, a1, a2, a3], 4) + ); + assert_eq!( + get_varargs::<4>(&a, args3, "test").unwrap(), + ([a1, a2, a3, NodePtr::NIL], 3) + ); + assert_eq!( + get_varargs::<4>(&a, args2, "test").unwrap(), + ([a2, a3, NodePtr::NIL, NodePtr::NIL], 2) + ); + assert_eq!( + get_varargs::<4>(&a, args1, "test").unwrap(), + ([a3, NodePtr::NIL, NodePtr::NIL, NodePtr::NIL], 1) + ); + assert_eq!( + get_varargs::<4>(&a, args0, "test").unwrap(), + ([NodePtr::NIL; 4], 0) + ); + + let r = get_varargs::<3>(&a, args4, "test").unwrap_err(); + assert_eq!(r.0, args4); + assert_eq!(r.1, "test takes no more than 3 arguments"); + + let r = get_varargs::<1>(&a, args4, "test").unwrap_err(); + assert_eq!(r.0, args4); + assert_eq!(r.1, "test takes no more than 1 argument"); + } + + #[test] + fn test_nilp() { + let mut a = Allocator::new(); + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let a3 = a.new_number(0.into()).unwrap(); + let a4 = a.new_atom(&[]).unwrap(); + let a5 = a.nil(); + let pair = a.new_pair(a0, a1).unwrap(); + assert!(!nilp(&a, pair)); + assert!(!nilp(&a, a0)); + assert!(!nilp(&a, a1)); + assert!(nilp(&a, a3)); + assert!(nilp(&a, a4)); + assert!(nilp(&a, a5)); + } + + #[test] + fn test_first() { + let mut a = Allocator::new(); + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let pair = a.new_pair(a0, a1).unwrap(); + assert_eq!(first(&a, pair).unwrap(), a0); + + let r = first(&a, a0).unwrap_err(); + assert_eq!(r.0, a0); + assert_eq!(r.1, "first of non-cons"); + } + + #[test] + fn test_rest() { + let mut a = Allocator::new(); + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let pair = a.new_pair(a0, a1).unwrap(); + assert_eq!(rest(&a, pair).unwrap(), a1); + + let r = rest(&a, a0).unwrap_err(); + assert_eq!(r.0, a0); + assert_eq!(r.1, "rest of non-cons"); + } + + #[rstest] + #[case(0.into(), (0.into(), 0))] + #[case(1.into(), (1.into(), 1))] + #[case(42.into(), (42.into(), 1))] + #[case(1337.into(), (1337.into(), 2))] + #[case(0x5fffff.into(), (0x5fffff.into(), 3))] + #[case(0xffffff.into(), (0xffffff.into(), 4))] + fn test_int_atom(#[case] value: Number, #[case] expected: (Number, usize)) { + let mut a = Allocator::new(); + let a0 = a.new_number(value).unwrap(); + assert_eq!(int_atom(&a, a0, "test").unwrap(), expected); + } + + #[test] + fn test_int_atom_failure() { + let mut a = Allocator::new(); + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let pair = a.new_pair(a0, a1).unwrap(); + let r = int_atom(&a, pair, "test").unwrap_err(); + assert_eq!(r.0, pair); + assert_eq!(r.1, "test requires int args"); + } + + #[test] + fn test_atom_len() { + let mut a = Allocator::new(); + + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + let pair = a.new_pair(a0, a1).unwrap(); + + let r = atom_len(&a, pair, "test").unwrap_err(); + assert_eq!(r.0, pair); + assert_eq!(r.1, "test requires an atom"); + + assert_eq!(atom_len(&a, a0, "test").unwrap(), 1); + assert_eq!(atom_len(&a, a1, "test").unwrap(), 2); + } + + // u32, 4 bytes + #[rstest] + #[case(&[0], 0)] + #[case(&[0,0,0,1], 1)] + #[case(&[0,0xff,0xff,0xff,0xff], 0xffffffff)] + #[case(&[0,0,0,0,0,0xff,0xff,0xff,0xff], 0xffffffff)] + #[case(&[0x7f,0xff], 0x7fff)] + #[case(&[0x7f,0xff, 0xff], 0x7fffff)] + #[case(&[0x7f,0xff,0xff, 0xff], 0x7fffffff)] + #[case(&[0x01,0x02,0x03, 0x04], 0x1020304)] + #[case(&[] as &[u8], 0)] + fn test_uint_atom_4_success(#[case] buf: &[u8], #[case] expected: u64) { + use crate::allocator::Allocator; + let mut a = Allocator::new(); + let n = a.new_atom(buf).unwrap(); + assert!(uint_atom::<4>(&a, n, "test") == Ok(expected)); + } + + // u32, 4 bytes + #[rstest] + #[case(&[0xff,0xff,0xff,0xff], "test requires positive int arg")] + #[case(&[0xff], "test requires positive int arg")] + #[case(&[0x80], "test requires positive int arg")] + #[case(&[0x80,0,0,0], "test requires positive int arg")] + #[case(&[1, 0xff,0xff,0xff,0xff], "test requires u32 arg")] + fn test_uint_atom_4_failure(#[case] buf: &[u8], #[case] expected: &str) { + use crate::allocator::Allocator; + let mut a = Allocator::new(); + let n = a.new_atom(buf).unwrap(); + assert!(uint_atom::<4>(&a, n, "test") == err(n, expected)); + } + + #[test] + fn test_uint_atom_4_pair() { + use crate::allocator::Allocator; + let mut a = Allocator::new(); + let n = a.new_atom(&[0, 0]).unwrap(); + let p = a.new_pair(n, n).unwrap(); + assert!(uint_atom::<4>(&a, p, "test") == err(p, "test requires int arg")); + } + + // u64, 8 bytes + #[rstest] + #[case(&[0], 0)] + #[case(&[0,0,0,1], 1)] + #[case(&[0,0xff,0xff,0xff,0xff], 0xffffffff)] + #[case(&[0,0,0,0,0xff,0xff,0xff,0xff], 0xffffffff)] + #[case(&[0x7f, 0xff], 0x7fff)] + #[case(&[0x7f, 0xff, 0xff], 0x7fffff)] + #[case(&[0x7f, 0xff,0xff, 0xff], 0x7fffffff)] + #[case(&[0x7f, 0xff,0xff, 0xff, 0xff], 0x7fffffffff)] + #[case(&[0x7f, 0xff,0xff, 0xff, 0xff, 0xff], 0x7fffffffffff)] + #[case(&[0x7f, 0xff,0xff, 0xff, 0xff, 0xff, 0xff], 0x7fffffffffffff)] + #[case(&[0x7f, 0xff,0xff, 0xff, 0xff, 0xff, 0xff, 0xff], 0x7fffffffffffffff)] + #[case(&[0x01, 0x02,0x03, 0x04, 0x05, 0x06, 0x07, 0x08 ], 0x102030405060708)] + #[case(&[] as &[u8], 0)] + fn test_uint_atom_8_success(#[case] buf: &[u8], #[case] expected: u64) { + use crate::allocator::Allocator; + let mut a = Allocator::new(); + let n = a.new_atom(buf).unwrap(); + assert!(uint_atom::<8>(&a, n, "test") == Ok(expected)); + } + + // u64, 8 bytes + #[rstest] + #[case(&[0xff,0xff,0xff,0xff], "test requires positive int arg")] + #[case(&[0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff], "test requires positive int arg")] + #[case(&[0xff], "test requires positive int arg")] + #[case(&[0x80], "test requires positive int arg")] + #[case(&[0x80,0,0,0], "test requires positive int arg")] + #[case(&[1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff], "test requires u64 arg")] + fn test_uint_atom_8_failure(#[case] buf: &[u8], #[case] expected: &str) { + use crate::allocator::Allocator; + let mut a = Allocator::new(); + let n = a.new_atom(buf).unwrap(); + assert!(uint_atom::<8>(&a, n, "test") == err(n, expected)); + } + + #[test] + fn test_uint_atom_8_pair() { + use crate::allocator::Allocator; + let mut a = Allocator::new(); + let n = a.new_atom(&[0, 0]).unwrap(); + let p = a.new_pair(n, n).unwrap(); + assert!(uint_atom::<8>(&a, p, "test") == err(p, "test requires int arg")); + } + + #[test] + fn test_u32_from_u8() { + assert_eq!(u32_from_u8(&[]), Some(0)); + assert_eq!(u32_from_u8(&[0xcc]), Some(0xcc)); + assert_eq!(u32_from_u8(&[0xcc, 0x55]), Some(0xcc55)); + assert_eq!(u32_from_u8(&[0xcc, 0x55, 0x88]), Some(0xcc5588)); + assert_eq!(u32_from_u8(&[0xcc, 0x55, 0x88, 0xf3]), Some(0xcc5588f3)); + + assert_eq!(u32_from_u8(&[0xff]), Some(0xff)); + assert_eq!(u32_from_u8(&[0xff, 0xff]), Some(0xffff)); + assert_eq!(u32_from_u8(&[0xff, 0xff, 0xff]), Some(0xffffff)); + assert_eq!(u32_from_u8(&[0xff, 0xff, 0xff, 0xff]), Some(0xffffffff)); + + // leading zeros are not stripped, and not allowed beyond 4 bytes + assert_eq!(u32_from_u8(&[0x00]), Some(0)); + assert_eq!(u32_from_u8(&[0x00, 0x00]), Some(0)); + assert_eq!(u32_from_u8(&[0x00, 0xcc, 0x55, 0x88]), Some(0xcc5588)); + assert_eq!(u32_from_u8(&[0x00, 0x00, 0xcc, 0x55, 0x88]), None); + assert_eq!(u32_from_u8(&[0x00, 0xcc, 0x55, 0x88, 0xf3]), None); + + // overflow, too many bytes + assert_eq!(u32_from_u8(&[0x01, 0xcc, 0x55, 0x88, 0xf3]), None); + assert_eq!(u32_from_u8(&[0x01, 0x00, 0x00, 0x00, 0x00]), None); + assert_eq!(u32_from_u8(&[0x7d, 0xcc, 0x55, 0x88, 0xf3]), None); + } + + #[test] + fn test_i32_from_u8() { + assert_eq!(i32_from_u8(&[]), Some(0)); + assert_eq!(i32_from_u8(&[0xcc]), Some(-52)); + assert_eq!(i32_from_u8(&[0xcc, 0x55]), Some(-13227)); + assert_eq!(i32_from_u8(&[0xcc, 0x55, 0x88]), Some(-3385976)); + assert_eq!(i32_from_u8(&[0xcc, 0x55, 0x88, 0xf3]), Some(-866809613)); + + assert_eq!(i32_from_u8(&[0xff]), Some(-1)); + assert_eq!(i32_from_u8(&[0xff, 0xff]), Some(-1)); + assert_eq!(i32_from_u8(&[0xff, 0xff, 0xff]), Some(-1)); + assert_eq!(i32_from_u8(&[0xff, 0xff, 0xff, 0xff]), Some(-1)); + + // leading zeros are not stripped, and not allowed beyond 4 bytes + assert_eq!(i32_from_u8(&[0x00]), Some(0)); + assert_eq!(i32_from_u8(&[0x00, 0x00]), Some(0)); + assert_eq!(i32_from_u8(&[0x00, 0xcc, 0x55, 0x88]), Some(0xcc5588)); + assert_eq!(i32_from_u8(&[0x00, 0x00, 0xcc, 0x55, 0x88]), None); + assert_eq!(i32_from_u8(&[0x00, 0xcc, 0x55, 0x88, 0xf3]), None); + + // overflow, it doesn't really matter whether the bytes are 0 or not, any + // atom larger than 4 bytes is rejected + assert_eq!(i32_from_u8(&[0x01, 0xcc, 0x55, 0x88, 0xf3]), None); + assert_eq!(i32_from_u8(&[0x01, 0x00, 0x00, 0x00, 0x00]), None); + assert_eq!(i32_from_u8(&[0x7d, 0xcc, 0x55, 0x88, 0xf3]), None); + } + + #[test] + fn test_u64_from_bytes() { + assert_eq!(u64_from_bytes(&[]), 0); + assert_eq!(u64_from_bytes(&[0xcc]), 0xcc); + assert_eq!(u64_from_bytes(&[0xcc, 0x55]), 0xcc55); + assert_eq!(u64_from_bytes(&[0xcc, 0x55, 0x88]), 0xcc5588); + assert_eq!(u64_from_bytes(&[0xcc, 0x55, 0x88, 0xf3]), 0xcc5588f3); + + assert_eq!(u64_from_bytes(&[0xff]), 0xff); + assert_eq!(u64_from_bytes(&[0xff, 0xff]), 0xffff); + assert_eq!(u64_from_bytes(&[0xff, 0xff, 0xff]), 0xffffff); + assert_eq!(u64_from_bytes(&[0xff, 0xff, 0xff, 0xff]), 0xffffffff); + + assert_eq!(u64_from_bytes(&[0x00]), 0); + assert_eq!(u64_from_bytes(&[0x00, 0x00]), 0); + assert_eq!(u64_from_bytes(&[0x00, 0xcc, 0x55, 0x88]), 0xcc5588); + assert_eq!(u64_from_bytes(&[0x00, 0x00, 0xcc, 0x55, 0x88]), 0xcc5588); + assert_eq!(u64_from_bytes(&[0x00, 0xcc, 0x55, 0x88, 0xf3]), 0xcc5588f3); + + assert_eq!( + u64_from_bytes(&[0xcc, 0x55, 0x88, 0xf3, 0xcc, 0x55, 0x88, 0xf3]), + 0xcc5588f3cc5588f3 + ); + } + + #[test] + fn test_i32_atom() { + let mut a = Allocator::new(); + + let a0 = a.new_number(42.into()).unwrap(); + let a1 = a.new_number(1337.into()).unwrap(); + + let pair = a.new_pair(a0, a1).unwrap(); + + let r = i32_atom(&a, pair, "test").unwrap_err(); + assert_eq!(r.0, pair); + assert_eq!(r.1, "test requires int32 args"); + + assert_eq!(i32_atom(&a, a0, "test").unwrap(), 42); + assert_eq!(i32_atom(&a, a1, "test").unwrap(), 1337); + + let a2 = a.new_number(0x100000000_i64.into()).unwrap(); + let r = i32_atom(&a, a2, "test").unwrap_err(); + assert_eq!(r.0, a2); + assert_eq!(r.1, "test requires int32 args (with no leading zeros)"); + + let a3 = a.new_number((-0xffffffff_i64).into()).unwrap(); + let r = i32_atom(&a, a3, "test").unwrap_err(); + assert_eq!(r.0, a3); + assert_eq!(r.1, "test requires int32 args (with no leading zeros)"); + } +} diff --git a/src/run_program.rs b/src/run_program.rs index 8fac95b0..f6b70f19 100644 --- a/src/run_program.rs +++ b/src/run_program.rs @@ -546,968 +546,964 @@ pub fn run_program_with_counters<'a, D: Dialect>( } #[cfg(test)] -struct RunProgramTest<'a> { - prg: &'a str, - args: &'a str, - flags: u32, - result: Option<&'a str>, - cost: Cost, - err: &'a str, -} +mod tests { + use super::*; -#[cfg(test)] -use crate::test_ops::parse_exp; + use crate::chia_dialect::{ENABLE_BLS_OPS_OUTSIDE_GUARD, NO_UNKNOWN_OPS}; + use crate::test_ops::parse_exp; -#[cfg(test)] -use crate::chia_dialect::{ENABLE_BLS_OPS_OUTSIDE_GUARD, NO_UNKNOWN_OPS}; + use rstest::rstest; -#[cfg(test)] -const TEST_CASES: &[RunProgramTest] = &[ - RunProgramTest { - prg: "(/ (q . 10) (q . -3))", - args: "()", - flags: 0, - result: Some("-4"), - cost: 1047, - err: "", - }, - RunProgramTest { - prg: "(/ (q . -10) (q . 3))", - args: "()", - flags: 0, - result: Some("-4"), - cost: 1047, - err: "", - }, - RunProgramTest { - prg: "(/ (q . -1) (q . 2))", - args: "()", - flags: 0, - result: Some("-1"), - cost: 1047, - err: "", - }, - // (mod (X N) (defun power (X N) (if (= N 0) 1 (* X (power X (- N 1))))) (power X N)) - RunProgramTest { - prg: "(a (q 2 2 (c 2 (c 5 (c 11 ())))) (c (q 2 (i (= 11 ()) (q 1 . 1) (q 18 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ())))))) 1) 1))", - args: "(5033 1000)", - flags: 0, - result: Some("0x024d4f505f1f813ca5e0ae8805bad8707347e65c5f7595da4852be5074288431d1df11a0c326d249f1f52ee051579403d1d0c23a7a1e9af18b7d7dc4c63c73542863c434ae9dfa80141a30cf4acee0d6c896aa2e64ea748404427a3bdaa1b97e4e09b8f5e4f8e9c568a4fc219532dbbad5ec54476d19b7408f8e7e7df16b830c20a1e83d90cc0620b0677b7606307f725539ef223561cdb276baf8e92156ee6492d97159c8f64768349ea7e219fd07fa818a59d81d0563b140396402f0ff758840da19808440e0a57c94c48ef84b4ab7ca8c5f010b69b8f443b12b50bd91bdcf2a96208ddac283fa294d6a99f369d57ab41d03eab5bb4809223c141ad94378516e6766a5054e22e997e260978af68a86893890d612f081b40d54fd1e940af35c0d7900c9a917e2458a61ef8a83f7211f519b2c5f015dfa7c2949ef8bedd02d3bad64ca9b2963dc2bb79f24092331133a7a299872079b9d0422b8fc0eeba4e12c7667ac7282cc6ff98a7c670614c9fce5a061b8d5cd4dd3c6d62d245688b62f9713dc2604bdd5bbc85c070c51f784a9ebac0e0eaa2e29e82d93e570887aa7e1a9d25baf0b2c55a4615f35ec0dbe9baa921569700f95e10cd2d4f6ba152a2ac288c37b60980df33dadfa920fd43dbbf55a0b333b88a3237d954e33d80ed6582019faf51db5f1b52e392559323f8bdd945e7fc6cb8f97f2b8417cfc184d7bfbfa5314d4114f95b725847523f1848d13c28ad96662298ee4e2d87af23e7cb4e58d7a20a5c57ae6833b4a37dcafccca0245a0d6ef28f83200d74db390281e03dd3a8b782970895764c3fcef31c5ed6d0b6e4e796a62ad5654691eea0d9db351cc4fee63248405b24c98bd5e68e4a5e0ab11e90e3c7de270c594d3a35639d931853b7010c8c896f6b28b2af719e53da65da89d44b926b6f06123c9217a43be35d751516bd02c18c4f868a2eae78ae3c6deab1115086c8ce58414db4561865d17ab95c7b3d4e1bfc6d0a4d3fbf5f20a0a7d77a9270e4da354c588da55b0063aec76654019ffb310e1503d99a7bc81ccdf5f8b15c8638156038624cf35988d8420bfdb59184c4b86bf5448df65c44aedc2e98eead7f1ba4be8f402baf12d41076b8f0991cfc778e04ba2c05d1440c70488ffaeefde537064035037f729b683e8ff1b3d0b4aa26a2b30bcaa9379f7fcc7072ff9a2c3e801c5979b0ab3e7acf89373de642d596f26514b9fa213ca217181a8429ad69d14445a822b16818c2509480576dc0ff7bac48c557e6d1883039f4daf873fa4f9a4d849130e2e4336049cfaf9e69a7664f0202b901cf07c7065c4dc93c46f98c5ea5c9c9d911b733093490da3bf1c95f43cd18b7be3798535a55ac6da3442946a268b74bde1349ca9807c41d90c7ec218a17efd2c21d5fcd720501f8a488f1dfba0a423dfdb2a877707b77930e80d734ceabcdb24513fad8f2e2470604d041df083bf184edd0e9720dd2b608b1ee1df951d7ce8ec671317b4f5a3946aa75280658b4ef77b3f504ce73e7ecac84eec3c2b45fb62f6fbd5ab78c744abd3bf5d0ab37d7b19124d2470d53db09ddc1f9dd9654b0e6a3a44c95d0a5a5e061bd24813508d3d1c901544dc3e6b84ca38dd2fde5ea60a57cbc12428848c4e3f6fd4941ebd23d709a717a090dd01830436659f7c20fd2d70c916427e9f3f12ac479128c2783f02a9824aa4e31de133c2704e049a50160f656e28aa0a2615b32bd48bb5d5d13d363a487324c1e9b8703be938bc545654465c9282ad5420978263b3e3ba1bb45e1a382554ac68e5a154b896c9c4c2c3853fbbfc877c4fb7dc164cc420f835c413839481b1d2913a68d206e711fb19b284a7bb2bd2033531647cf135833a0f3026b0c1dc0c184120d30ef4865985fdacdfb848ab963d2ae26a784b7b6a64fdb8feacf94febed72dcd0a41dc12be26ed79af88f1d9cba36ed1f95f2da8e6194800469091d2dfc7b04cfe93ab7a7a888b2695bca45a76a1458d08c3b6176ab89e7edc56c7e01142adfff944641b89cd5703a911145ac4ec42164d90b6fcd78b39602398edcd1f935485894fb8a1f416e031624806f02fbd07f398dbfdd48b86dfacf2045f85ecfe5bb1f01fae758dcdb4ae3b1e2aac6f0878f700d1f430b8ca47c9d8254059bd5c006042c4605f33ca98b41"), - cost: 15073165, - err: "", - }, - // ' - RunProgramTest { - prg: "(= (point_add (pubkey_for_exp (q . -2)) (pubkey_for_exp (q . 5))) (pubkey_for_exp (q . 3)))", - args: "()", - flags: 0, - result: Some("1"), - cost: 6768556, - err: "", - }, - RunProgramTest { - prg: "(= (point_add (pubkey_for_exp (q . 2)) (pubkey_for_exp (q . 3))) (pubkey_for_exp (q . 5)))", - args: "()", - flags: 0, - result: Some("1"), - cost: 6768556, - err: "", - }, - RunProgramTest { - prg: "(point_add (pubkey_for_exp (q . 1)) (pubkey_for_exp (q . 2)))", - args: "()", - flags: 0, - result: Some("0x89ece308f9d1f0131765212deca99697b112d61f9be9a5f1f3780a51335b3ff981747a0b2ca2179b96d2c0c9024e5224"), - cost: 5442073, - err: "", - }, - RunProgramTest { - prg: "(f (f (q . ((100 200 300) 400 500))))", - args: "()", - flags: 0, - result: Some("0x64"), - cost: 82, - err: "", - }, - RunProgramTest { - prg: "(= (f 1) (+ (f (r 1)) (f (r (r 1)))))", - args: "(7 3 3)", - flags: 0, - result: Some("()"), - cost: 1194, - err: "", - }, - RunProgramTest { - prg: "(= (f 1) (+ (f (r 1)) (f (r (r 1)))))", - args: "(7 3 4)", - flags: 0, - result: Some("1"), - cost: 1194, - err: "", - }, - RunProgramTest { - prg: "(i (f (r (r 1))) (f 1) (f (r 1)))", - args: "(200 300 400)", - flags: 0, - result: Some("0x00c8"), - cost: 352, - err: "", - }, - RunProgramTest { - prg: "(i (f (r (r 1))) (f 1) (f (r 1)))", - args: "(200 300 1)", - flags: 0, - result: Some("0x00c8"), - cost: 352, - err: "", - }, - RunProgramTest { - prg: "(r (r (q . ((100 200 300) 400 500))))", - args: "()", - flags: 0, - result: Some("(500)"), - cost: 82, - err: "", - }, - RunProgramTest { - prg: "(* (q . 10000000000000000000000000000000000) (q . 10000000000000000000000000000000) (q . 100000000000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000))", - args: "()", - flags: 0, - result: Some("0x04261a5c969abab851babdb4f178e63bf2ed3879fc13a4c75622d73c909440a4763849b52e49cd2522500f555f6a3131775f93ddcf24eda7a1dbdf828a033626da873caaaa880a9121f4c44a157973f60443dc53bc99ac12d5bd5fa20a88320ae2ccb8e1b5e792cbf0d001bb0fbd7765d3936e412e2fc8f1267833237237fcb638dda0a7aa674680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - cost: 24255, - err: "", - }, - - // ## APPLY - RunProgramTest { - prg: "(a (q 0x0fffffffff) (q ()))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "invalid operator", - }, - RunProgramTest { - prg: "(a (q . 0) (q . 1) (q . 2))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "apply takes exactly 2 arguments", - }, - RunProgramTest { - prg: "(a (q 0x00ffffffffffffffffffff00) (q ()))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "invalid operator", - }, - RunProgramTest { - prg: "(a (q . 1))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "apply takes exactly 2 arguments", - }, - RunProgramTest { - prg: "(a (q . 1) (q . (100 200)))", - args: "()", - flags: 0, - result: Some("(100 200)"), - cost: 175, - err: "", - }, - RunProgramTest { - prg: "(a (q . (+ 2 5)) (q . (20 30)))", - args: "()", - flags: 0, - result: Some("50"), - cost: 987, - err: "", - }, - RunProgramTest { - prg: "((c (q . (+ (q . 50) 1)) (q . 500)))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "in the ((X)...) syntax, the inner list takes exactly 1 argument", - }, - RunProgramTest { - prg: "((#c) (q . 3) (q . 4))", - args: "()", - flags: 0, - result: Some("((1 . 3) 1 . 4)"), - cost: 140, - err: "", - }, - RunProgramTest { - prg: "((#+) 1 2 3)", - args: "()", - flags: 0, - result: Some("6"), - cost: 1168, - err: "", - }, - RunProgramTest { - prg: "(a (q . 2) (q . (3 4 5)))", - args: "()", - flags: 0, - result: Some("3"), - cost: 179, - err: "", - }, - - // ## PATH LOOKUPS - - // 0 - RunProgramTest { - prg: "0", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("()"), - cost: 44, - err: "", - }, - // 1 - RunProgramTest { - prg: "1", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("(((8 . 12) 10 . 14) (9 . 13) 11 . 15)"), - cost: 44, - err: "", - }, - // 2 - RunProgramTest { - prg: "2", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("((8 . 12) 10 . 14)"), - cost: 48, - err: "", - }, - // 3 - RunProgramTest { - prg: "3", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("((9 . 13) 11 . 15)"), - cost: 48, - err: "", - }, - // 4 - RunProgramTest { - prg: "4", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("(8 . 12)"), - cost: 52, - err: "", - }, - // 5 - RunProgramTest { - prg: "5", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("(9 . 13)"), - cost: 52, - err: "", - }, - // 6 - RunProgramTest { - prg: "6", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("(10 . 14)"), - cost: 52, - err: "", - }, - // 7 - RunProgramTest { - prg: "7", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("(11 . 15)"), - cost: 52, - err: "", - }, - RunProgramTest { - prg: "8", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("8"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "9", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("9"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "10", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("10"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "11", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("11"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "12", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("12"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "13", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("13"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "14", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("14"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "15", - args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", - flags: 0, - result: Some("15"), - cost: 56, - err: "", - }, - RunProgramTest { - prg: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", - args: "(((0x1337 . (0x1337 . (42 . 0x1337))) . 0x1337) . 0x1337)", - flags: 0, - result: Some("(((0x1337 . (0x1337 . (42 . 0x1337))) . 0x1337) . 0x1337)"), - cost: 536, - err: "", - }, - RunProgramTest { - prg: "0x0000C8C141AB3121E776", - args: "((0x1337 . (0x1337 . ((0x1337 . (0x1337 . (0x1337 . ((0x1337 . (0x1337 . (0x1337 . (((0x1337 . (0x1337 . (0x1337 . (0x1337 . (((((0x1337 . (((0x1337 . ((((0x1337 . (0x1337 . (((0x1337 . (0x1337 . ((0x1337 . ((0x1337 . ((0x1337 . (0x1337 . ((((((0x1337 . ((0x1337 . ((((((0x1337 . (0x1337 . ((((0x1337 . (((0x1337 . 42) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337))) . 0x1337) . 0x1337) . 0x1337) . 0x1337) . 0x1337)) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337) . 0x1337))) . 0x1337)) . 0x1337)) . 0x1337))) . 0x1337) . 0x1337))) . 0x1337) . 0x1337) . 0x1337)) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337))))) . 0x1337) . 0x1337)))) . 0x1337)))) . 0x1337))) . 0x1337)", - flags: 0, - result: Some("42"), - cost: 304, - err: "", - }, - RunProgramTest { - prg: "7708975405620101644641102810267383005", - args: "(0x1337 . ((0x1337 . (0x1337 . (0x1337 . ((0x1337 . (0x1337 . (((0x1337 . ((0x1337 . (0x1337 . (0x1337 . (0x1337 . (0x1337 . ((0x1337 . (0x1337 . ((0x1337 . (((0x1337 . (0x1337 . (0x1337 . ((0x1337 . (((0x1337 . (((0x1337 . (0x1337 . (0x1337 . (0x1337 . ((0x1337 . ((0x1337 . (((((0x1337 . ((0x1337 . ((0x1337 . (0x1337 . (0x1337 . (((0x1337 . (0x1337 . ((0x1337 . (0x1337 . ((((0x1337 . (0x1337 . (0x1337 . (0x1337 . (((((0x1337 . (0x1337 . (0x1337 . (0x1337 . (0x1337 . (((((0x1337 . (((((0x1337 . ((0x1337 . (0x1337 . ((((0x1337 . ((((0x1337 . ((0x1337 . ((0x1337 . ((0x1337 . (0x1337 . (0x1337 . ((((0x1337 . (0x1337 . ((0x1337 . (((0x1337 . (0x1337 . (((0x1337 . (0x1337 . (0x1337 . (42 . 0x1337)))) . 0x1337) . 0x1337))) . 0x1337) . 0x1337)) . 0x1337))) . 0x1337) . 0x1337) . 0x1337)))) . 0x1337)) . 0x1337)) . 0x1337)) . 0x1337) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337))) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337)))))) . 0x1337) . 0x1337) . 0x1337) . 0x1337))))) . 0x1337) . 0x1337) . 0x1337))) . 0x1337))) . 0x1337) . 0x1337)))) . 0x1337)) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337)) . 0x1337)) . 0x1337))))) . 0x1337) . 0x1337)) . 0x1337) . 0x1337)) . 0x1337)))) . 0x1337) . 0x1337)) . 0x1337))) . 0x1337)))))) . 0x1337)) . 0x1337) . 0x1337))) . 0x1337)))) . 0x1337))", - flags: 0, - result: Some("42"), - cost: 532, - err: "", - }, - RunProgramTest { - prg: "1", - args: "1", - flags: 0, - result: Some("1"), - cost: 44, - err: "", - }, - RunProgramTest { - prg: "(> 3 3)", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "path into atom", - }, - - // ## SOFTFORK - - // the arguments to softfork are checked in mempool mode, but in consensus - // mode, only the cost argument is - RunProgramTest { - prg: "(softfork (q . 979))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 979))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "softfork takes exactly 4 arguments", - }, - RunProgramTest { - prg: "(softfork (q . 959) (q . 9))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 959) (q . 9))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "softfork takes exactly 4 arguments", - }, - RunProgramTest { - prg: "(softfork (q . 939) (q . 9) (q x))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 939) (q . 9) (q x))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "softfork takes exactly 4 arguments", - }, - // this is a valid invocation, but we don't implement any extensions (yet) - // so the extension specifier 0 is still unknown - RunProgramTest { - prg: "(softfork (q . 919) (q . 9) (q x) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - // when parsing the cost argument, we ignore redundant leading zeroes - RunProgramTest { - prg: "(softfork (q . 0x00000397) (q . 9) (q x) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 919) (q . 9) (q x) (q . ()))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "unknown softfork extension", - }, - - // this is a valid invocation, but we don't implement any extensions (yet) - RunProgramTest { - prg: "(softfork (q . 919) (q . 0x00ffffffff) (q x) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 919) (q . 0x00ffffffff) (q x) (q . ()))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "unknown softfork extension", - }, - - // we don't allow negative "extension" parameters - RunProgramTest { - prg: "(softfork (q . 919) (q . -1) (q x) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 919) (q . -1) (q x) (q . ()))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "softfork requires positive int arg", - }, - - // we don't allow "extension" parameters > u32::MAX - RunProgramTest { - prg: "(softfork (q . 919) (q . 0x0100000000) (q x) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 919) (q . 0x0100000000) (q x) (q . ()))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "softfork requires u32 arg", - }, - - // we don't allow pairs as extension specifier - RunProgramTest { - prg: "(softfork (q . 919) (q 1 2 3) (q x) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1000, - err: "", - }, - RunProgramTest { - prg: "(softfork (q . 919) (q 1 2 3) (q x) (q . ()))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 1000, - err: "softfork requires int arg", - }, - - // the cost value is checked in consensus mode as well - RunProgramTest { - prg: "(softfork (q . 1000))", - args: "()", - flags: 0, - result: None, - cost: 1000, - err: "cost exceeded", - }, - // the cost parameter is mandatory - RunProgramTest { - prg: "(softfork)", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "first of non-cons", - }, - RunProgramTest { - prg: "(softfork (q . 0))", - args: "()", - flags: 0, - result: None, - cost: 1000, - err: "cost must be > 0", - }, - // negative costs are not allowed - RunProgramTest { - prg: "(softfork (q . -1))", - args: "()", - flags: 0, - result: None, - cost: 1000, - err: "softfork requires positive int arg", - }, - RunProgramTest { - prg: "(softfork (q 1 2 3))", - args: "()", - flags: 0, - result: None, - cost: 1000, - err: "softfork requires int arg", - }, - - // test mismatching cost - RunProgramTest { - prg: "(softfork (q . 160) (q . 0) (q . (q . 42)) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 241, - err: "", - }, - // the program under the softfork is restricted by the specified cost - RunProgramTest { - prg: "(softfork (q . 159) (q . 0) (q . (q . 42)) (q . ()))", - args: "()", - flags: 0, - result: None, - cost: 241, - err: "cost exceeded", - }, - // the cost specified on the softfork must match exactly the cost of - // executing the program - RunProgramTest { - prg: "(softfork (q . 161) (q . 0) (q . (q . 42)) (q . ()))", - args: "()", - flags: 0, - result: None, - cost: 10000, - err: "softfork specified cost mismatch", - }, - - // without the flag to enable the BLS extensions, it's an unknown extension - RunProgramTest { - prg: "(softfork (q . 161) (q . 1) (q . (q . 42)) (q . ()))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 10000, - err: "unknown softfork extension", - }, - - // coinid extension - // make sure we can execute the coinid operator under softfork 0 - // this program raises an exception if the computed coin ID matches the - // expected - RunProgramTest { - prg: "(softfork (q . 1432) (q . 0) (q a (i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q x) (q . 0)) (q . ())) (q . ()))", - args: "()", - flags: 0, - result: None, - cost: 1513, - err: "clvm raise", - }, - // also test the opposite. This program is the same as above but it raises - // if the coin ID is a mismatch - RunProgramTest { - prg: "(softfork (q . 1432) (q . 0) (q a (i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x)) (q . ())) (q . ()))", - args: "()", - flags: 0, - result: Some("()"), - cost: 1513, - err: "", - }, - - // coinid operator after hardfork, where coinid is available outside the - // softfork guard. - RunProgramTest { - prg: "(coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789))", - args: "()", - flags: ENABLE_BLS_OPS_OUTSIDE_GUARD, - result: Some("0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc"), - cost: 861, - err: "", - }, - RunProgramTest { - prg: "(coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 0x000123456789))", - args: "()", - flags: ENABLE_BLS_OPS_OUTSIDE_GUARD, - result: None, - cost: 861, - err: "coinid: invalid amount (may not have redundant leading zero)", - }, - // make sure the coinid operator is not available unless the flag is - // specified - RunProgramTest { - prg: "(coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 0x000123456789))", - args: "()", - flags: NO_UNKNOWN_OPS, - result: None, - cost: 861, - err: "unimplemented operator", - }, - - // secp261k1 - - RunProgramTest { - prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018))", - args: "()", - flags: 0, - result: Some("0"), - cost: 1300061, - err: "", - }, - // invalid signature - RunProgramTest { - prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c019))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "secp256k1_verify failed", - }, - - // secp261r1 - - RunProgramTest { - prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc))", - args: "()", - flags: 0, - result: Some("0"), - cost: 1850061, - err: "", - }, - // invalid signature - RunProgramTest { - prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffd))", - args: "()", - flags: 0, - result: None, - cost: 0, - err: "secp256r1_verify failed", - }, -]; + struct RunProgramTest<'a> { + prg: &'a str, + args: &'a str, + flags: u32, + result: Option<&'a str>, + cost: Cost, + err: &'a str, + } -#[cfg(test)] -fn check(res: (NodePtr, &str)) -> NodePtr { - assert_eq!(res.1, ""); - res.0 -} + const TEST_CASES: &[RunProgramTest] = &[ + RunProgramTest { + prg: "(/ (q . 10) (q . -3))", + args: "()", + flags: 0, + result: Some("-4"), + cost: 1047, + err: "", + }, + RunProgramTest { + prg: "(/ (q . -10) (q . 3))", + args: "()", + flags: 0, + result: Some("-4"), + cost: 1047, + err: "", + }, + RunProgramTest { + prg: "(/ (q . -1) (q . 2))", + args: "()", + flags: 0, + result: Some("-1"), + cost: 1047, + err: "", + }, + // (mod (X N) (defun power (X N) (if (= N 0) 1 (* X (power X (- N 1))))) (power X N)) + RunProgramTest { + prg: "(a (q 2 2 (c 2 (c 5 (c 11 ())))) (c (q 2 (i (= 11 ()) (q 1 . 1) (q 18 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ())))))) 1) 1))", + args: "(5033 1000)", + flags: 0, + result: Some("0x024d4f505f1f813ca5e0ae8805bad8707347e65c5f7595da4852be5074288431d1df11a0c326d249f1f52ee051579403d1d0c23a7a1e9af18b7d7dc4c63c73542863c434ae9dfa80141a30cf4acee0d6c896aa2e64ea748404427a3bdaa1b97e4e09b8f5e4f8e9c568a4fc219532dbbad5ec54476d19b7408f8e7e7df16b830c20a1e83d90cc0620b0677b7606307f725539ef223561cdb276baf8e92156ee6492d97159c8f64768349ea7e219fd07fa818a59d81d0563b140396402f0ff758840da19808440e0a57c94c48ef84b4ab7ca8c5f010b69b8f443b12b50bd91bdcf2a96208ddac283fa294d6a99f369d57ab41d03eab5bb4809223c141ad94378516e6766a5054e22e997e260978af68a86893890d612f081b40d54fd1e940af35c0d7900c9a917e2458a61ef8a83f7211f519b2c5f015dfa7c2949ef8bedd02d3bad64ca9b2963dc2bb79f24092331133a7a299872079b9d0422b8fc0eeba4e12c7667ac7282cc6ff98a7c670614c9fce5a061b8d5cd4dd3c6d62d245688b62f9713dc2604bdd5bbc85c070c51f784a9ebac0e0eaa2e29e82d93e570887aa7e1a9d25baf0b2c55a4615f35ec0dbe9baa921569700f95e10cd2d4f6ba152a2ac288c37b60980df33dadfa920fd43dbbf55a0b333b88a3237d954e33d80ed6582019faf51db5f1b52e392559323f8bdd945e7fc6cb8f97f2b8417cfc184d7bfbfa5314d4114f95b725847523f1848d13c28ad96662298ee4e2d87af23e7cb4e58d7a20a5c57ae6833b4a37dcafccca0245a0d6ef28f83200d74db390281e03dd3a8b782970895764c3fcef31c5ed6d0b6e4e796a62ad5654691eea0d9db351cc4fee63248405b24c98bd5e68e4a5e0ab11e90e3c7de270c594d3a35639d931853b7010c8c896f6b28b2af719e53da65da89d44b926b6f06123c9217a43be35d751516bd02c18c4f868a2eae78ae3c6deab1115086c8ce58414db4561865d17ab95c7b3d4e1bfc6d0a4d3fbf5f20a0a7d77a9270e4da354c588da55b0063aec76654019ffb310e1503d99a7bc81ccdf5f8b15c8638156038624cf35988d8420bfdb59184c4b86bf5448df65c44aedc2e98eead7f1ba4be8f402baf12d41076b8f0991cfc778e04ba2c05d1440c70488ffaeefde537064035037f729b683e8ff1b3d0b4aa26a2b30bcaa9379f7fcc7072ff9a2c3e801c5979b0ab3e7acf89373de642d596f26514b9fa213ca217181a8429ad69d14445a822b16818c2509480576dc0ff7bac48c557e6d1883039f4daf873fa4f9a4d849130e2e4336049cfaf9e69a7664f0202b901cf07c7065c4dc93c46f98c5ea5c9c9d911b733093490da3bf1c95f43cd18b7be3798535a55ac6da3442946a268b74bde1349ca9807c41d90c7ec218a17efd2c21d5fcd720501f8a488f1dfba0a423dfdb2a877707b77930e80d734ceabcdb24513fad8f2e2470604d041df083bf184edd0e9720dd2b608b1ee1df951d7ce8ec671317b4f5a3946aa75280658b4ef77b3f504ce73e7ecac84eec3c2b45fb62f6fbd5ab78c744abd3bf5d0ab37d7b19124d2470d53db09ddc1f9dd9654b0e6a3a44c95d0a5a5e061bd24813508d3d1c901544dc3e6b84ca38dd2fde5ea60a57cbc12428848c4e3f6fd4941ebd23d709a717a090dd01830436659f7c20fd2d70c916427e9f3f12ac479128c2783f02a9824aa4e31de133c2704e049a50160f656e28aa0a2615b32bd48bb5d5d13d363a487324c1e9b8703be938bc545654465c9282ad5420978263b3e3ba1bb45e1a382554ac68e5a154b896c9c4c2c3853fbbfc877c4fb7dc164cc420f835c413839481b1d2913a68d206e711fb19b284a7bb2bd2033531647cf135833a0f3026b0c1dc0c184120d30ef4865985fdacdfb848ab963d2ae26a784b7b6a64fdb8feacf94febed72dcd0a41dc12be26ed79af88f1d9cba36ed1f95f2da8e6194800469091d2dfc7b04cfe93ab7a7a888b2695bca45a76a1458d08c3b6176ab89e7edc56c7e01142adfff944641b89cd5703a911145ac4ec42164d90b6fcd78b39602398edcd1f935485894fb8a1f416e031624806f02fbd07f398dbfdd48b86dfacf2045f85ecfe5bb1f01fae758dcdb4ae3b1e2aac6f0878f700d1f430b8ca47c9d8254059bd5c006042c4605f33ca98b41"), + cost: 15073165, + err: "", + }, + // ' + RunProgramTest { + prg: "(= (point_add (pubkey_for_exp (q . -2)) (pubkey_for_exp (q . 5))) (pubkey_for_exp (q . 3)))", + args: "()", + flags: 0, + result: Some("1"), + cost: 6768556, + err: "", + }, + RunProgramTest { + prg: "(= (point_add (pubkey_for_exp (q . 2)) (pubkey_for_exp (q . 3))) (pubkey_for_exp (q . 5)))", + args: "()", + flags: 0, + result: Some("1"), + cost: 6768556, + err: "", + }, + RunProgramTest { + prg: "(point_add (pubkey_for_exp (q . 1)) (pubkey_for_exp (q . 2)))", + args: "()", + flags: 0, + result: Some("0x89ece308f9d1f0131765212deca99697b112d61f9be9a5f1f3780a51335b3ff981747a0b2ca2179b96d2c0c9024e5224"), + cost: 5442073, + err: "", + }, + RunProgramTest { + prg: "(f (f (q . ((100 200 300) 400 500))))", + args: "()", + flags: 0, + result: Some("0x64"), + cost: 82, + err: "", + }, + RunProgramTest { + prg: "(= (f 1) (+ (f (r 1)) (f (r (r 1)))))", + args: "(7 3 3)", + flags: 0, + result: Some("()"), + cost: 1194, + err: "", + }, + RunProgramTest { + prg: "(= (f 1) (+ (f (r 1)) (f (r (r 1)))))", + args: "(7 3 4)", + flags: 0, + result: Some("1"), + cost: 1194, + err: "", + }, + RunProgramTest { + prg: "(i (f (r (r 1))) (f 1) (f (r 1)))", + args: "(200 300 400)", + flags: 0, + result: Some("0x00c8"), + cost: 352, + err: "", + }, + RunProgramTest { + prg: "(i (f (r (r 1))) (f 1) (f (r 1)))", + args: "(200 300 1)", + flags: 0, + result: Some("0x00c8"), + cost: 352, + err: "", + }, + RunProgramTest { + prg: "(r (r (q . ((100 200 300) 400 500))))", + args: "()", + flags: 0, + result: Some("(500)"), + cost: 82, + err: "", + }, + RunProgramTest { + prg: "(* (q . 10000000000000000000000000000000000) (q . 10000000000000000000000000000000) (q . 100000000000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000) (q . 1000000000000000000000000000000))", + args: "()", + flags: 0, + result: Some("0x04261a5c969abab851babdb4f178e63bf2ed3879fc13a4c75622d73c909440a4763849b52e49cd2522500f555f6a3131775f93ddcf24eda7a1dbdf828a033626da873caaaa880a9121f4c44a157973f60443dc53bc99ac12d5bd5fa20a88320ae2ccb8e1b5e792cbf0d001bb0fbd7765d3936e412e2fc8f1267833237237fcb638dda0a7aa674680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + cost: 24255, + err: "", + }, -#[cfg(test)] -fn run_test_case(t: &RunProgramTest) { - use crate::chia_dialect::ChiaDialect; - use crate::test_ops::node_eq; - let mut allocator = Allocator::new(); - - let program = check(parse_exp(&mut allocator, t.prg)); - let args = check(parse_exp(&mut allocator, t.args)); - let expected_result = &t.result.map(|v| check(parse_exp(&mut allocator, v))); - - let dialect = ChiaDialect::new(t.flags); - println!("prg: {}", t.prg); - match run_program(&mut allocator, &dialect, program, args, t.cost) { - Ok(Reduction(cost, prg_result)) => { - assert!(node_eq(&allocator, prg_result, expected_result.unwrap())); - assert_eq!(cost, t.cost); - - // now, run the same program again but with the cost limit 1 too low, to - // ensure it fails with the correct error - let expected_cost_exceeded = - run_program(&mut allocator, &dialect, program, args, t.cost - 1).unwrap_err(); - assert_eq!(expected_cost_exceeded.1, "cost exceeded"); - } - Err(err) => { - println!("FAILED: {}", err.1); - assert_eq!(err.1, t.err); - assert!(expected_result.is_none()); + // ## APPLY + RunProgramTest { + prg: "(a (q 0x0fffffffff) (q ()))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "invalid operator", + }, + RunProgramTest { + prg: "(a (q . 0) (q . 1) (q . 2))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "apply takes exactly 2 arguments", + }, + RunProgramTest { + prg: "(a (q 0x00ffffffffffffffffffff00) (q ()))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "invalid operator", + }, + RunProgramTest { + prg: "(a (q . 1))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "apply takes exactly 2 arguments", + }, + RunProgramTest { + prg: "(a (q . 1) (q . (100 200)))", + args: "()", + flags: 0, + result: Some("(100 200)"), + cost: 175, + err: "", + }, + RunProgramTest { + prg: "(a (q . (+ 2 5)) (q . (20 30)))", + args: "()", + flags: 0, + result: Some("50"), + cost: 987, + err: "", + }, + RunProgramTest { + prg: "((c (q . (+ (q . 50) 1)) (q . 500)))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "in the ((X)...) syntax, the inner list takes exactly 1 argument", + }, + RunProgramTest { + prg: "((#c) (q . 3) (q . 4))", + args: "()", + flags: 0, + result: Some("((1 . 3) 1 . 4)"), + cost: 140, + err: "", + }, + RunProgramTest { + prg: "((#+) 1 2 3)", + args: "()", + flags: 0, + result: Some("6"), + cost: 1168, + err: "", + }, + RunProgramTest { + prg: "(a (q . 2) (q . (3 4 5)))", + args: "()", + flags: 0, + result: Some("3"), + cost: 179, + err: "", + }, + + // ## PATH LOOKUPS + + // 0 + RunProgramTest { + prg: "0", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("()"), + cost: 44, + err: "", + }, + // 1 + RunProgramTest { + prg: "1", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("(((8 . 12) 10 . 14) (9 . 13) 11 . 15)"), + cost: 44, + err: "", + }, + // 2 + RunProgramTest { + prg: "2", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("((8 . 12) 10 . 14)"), + cost: 48, + err: "", + }, + // 3 + RunProgramTest { + prg: "3", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("((9 . 13) 11 . 15)"), + cost: 48, + err: "", + }, + // 4 + RunProgramTest { + prg: "4", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("(8 . 12)"), + cost: 52, + err: "", + }, + // 5 + RunProgramTest { + prg: "5", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("(9 . 13)"), + cost: 52, + err: "", + }, + // 6 + RunProgramTest { + prg: "6", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("(10 . 14)"), + cost: 52, + err: "", + }, + // 7 + RunProgramTest { + prg: "7", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("(11 . 15)"), + cost: 52, + err: "", + }, + RunProgramTest { + prg: "8", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("8"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "9", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("9"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "10", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("10"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "11", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("11"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "12", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("12"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "13", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("13"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "14", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("14"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "15", + args: "(((8 . 12) . (10 . 14)) . ((9 . 13) . (11 . 15)))", + flags: 0, + result: Some("15"), + cost: 56, + err: "", + }, + RunProgramTest { + prg: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + args: "(((0x1337 . (0x1337 . (42 . 0x1337))) . 0x1337) . 0x1337)", + flags: 0, + result: Some("(((0x1337 . (0x1337 . (42 . 0x1337))) . 0x1337) . 0x1337)"), + cost: 536, + err: "", + }, + RunProgramTest { + prg: "0x0000C8C141AB3121E776", + args: "((0x1337 . (0x1337 . ((0x1337 . (0x1337 . (0x1337 . ((0x1337 . (0x1337 . (0x1337 . (((0x1337 . (0x1337 . (0x1337 . (0x1337 . (((((0x1337 . (((0x1337 . ((((0x1337 . (0x1337 . (((0x1337 . (0x1337 . ((0x1337 . ((0x1337 . ((0x1337 . (0x1337 . ((((((0x1337 . ((0x1337 . ((((((0x1337 . (0x1337 . ((((0x1337 . (((0x1337 . 42) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337))) . 0x1337) . 0x1337) . 0x1337) . 0x1337) . 0x1337)) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337) . 0x1337))) . 0x1337)) . 0x1337)) . 0x1337))) . 0x1337) . 0x1337))) . 0x1337) . 0x1337) . 0x1337)) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337))))) . 0x1337) . 0x1337)))) . 0x1337)))) . 0x1337))) . 0x1337)", + flags: 0, + result: Some("42"), + cost: 304, + err: "", + }, + RunProgramTest { + prg: "7708975405620101644641102810267383005", + args: "(0x1337 . ((0x1337 . (0x1337 . (0x1337 . ((0x1337 . (0x1337 . (((0x1337 . ((0x1337 . (0x1337 . (0x1337 . (0x1337 . (0x1337 . ((0x1337 . (0x1337 . ((0x1337 . (((0x1337 . (0x1337 . (0x1337 . ((0x1337 . (((0x1337 . (((0x1337 . (0x1337 . (0x1337 . (0x1337 . ((0x1337 . ((0x1337 . (((((0x1337 . ((0x1337 . ((0x1337 . (0x1337 . (0x1337 . (((0x1337 . (0x1337 . ((0x1337 . (0x1337 . ((((0x1337 . (0x1337 . (0x1337 . (0x1337 . (((((0x1337 . (0x1337 . (0x1337 . (0x1337 . (0x1337 . (((((0x1337 . (((((0x1337 . ((0x1337 . (0x1337 . ((((0x1337 . ((((0x1337 . ((0x1337 . ((0x1337 . ((0x1337 . (0x1337 . (0x1337 . ((((0x1337 . (0x1337 . ((0x1337 . (((0x1337 . (0x1337 . (((0x1337 . (0x1337 . (0x1337 . (42 . 0x1337)))) . 0x1337) . 0x1337))) . 0x1337) . 0x1337)) . 0x1337))) . 0x1337) . 0x1337) . 0x1337)))) . 0x1337)) . 0x1337)) . 0x1337)) . 0x1337) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337))) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337)))))) . 0x1337) . 0x1337) . 0x1337) . 0x1337))))) . 0x1337) . 0x1337) . 0x1337))) . 0x1337))) . 0x1337) . 0x1337)))) . 0x1337)) . 0x1337)) . 0x1337) . 0x1337) . 0x1337) . 0x1337)) . 0x1337)) . 0x1337))))) . 0x1337) . 0x1337)) . 0x1337) . 0x1337)) . 0x1337)))) . 0x1337) . 0x1337)) . 0x1337))) . 0x1337)))))) . 0x1337)) . 0x1337) . 0x1337))) . 0x1337)))) . 0x1337))", + flags: 0, + result: Some("42"), + cost: 532, + err: "", + }, + RunProgramTest { + prg: "1", + args: "1", + flags: 0, + result: Some("1"), + cost: 44, + err: "", + }, + RunProgramTest { + prg: "(> 3 3)", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "path into atom", + }, + + // ## SOFTFORK + + // the arguments to softfork are checked in mempool mode, but in consensus + // mode, only the cost argument is + RunProgramTest { + prg: "(softfork (q . 979))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 979))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "softfork takes exactly 4 arguments", + }, + RunProgramTest { + prg: "(softfork (q . 959) (q . 9))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 959) (q . 9))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "softfork takes exactly 4 arguments", + }, + RunProgramTest { + prg: "(softfork (q . 939) (q . 9) (q x))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 939) (q . 9) (q x))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "softfork takes exactly 4 arguments", + }, + // this is a valid invocation, but we don't implement any extensions (yet) + // so the extension specifier 0 is still unknown + RunProgramTest { + prg: "(softfork (q . 919) (q . 9) (q x) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + // when parsing the cost argument, we ignore redundant leading zeroes + RunProgramTest { + prg: "(softfork (q . 0x00000397) (q . 9) (q x) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 919) (q . 9) (q x) (q . ()))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "unknown softfork extension", + }, + + // this is a valid invocation, but we don't implement any extensions (yet) + RunProgramTest { + prg: "(softfork (q . 919) (q . 0x00ffffffff) (q x) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 919) (q . 0x00ffffffff) (q x) (q . ()))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "unknown softfork extension", + }, + + // we don't allow negative "extension" parameters + RunProgramTest { + prg: "(softfork (q . 919) (q . -1) (q x) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 919) (q . -1) (q x) (q . ()))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "softfork requires positive int arg", + }, + + // we don't allow "extension" parameters > u32::MAX + RunProgramTest { + prg: "(softfork (q . 919) (q . 0x0100000000) (q x) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 919) (q . 0x0100000000) (q x) (q . ()))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "softfork requires u32 arg", + }, + + // we don't allow pairs as extension specifier + RunProgramTest { + prg: "(softfork (q . 919) (q 1 2 3) (q x) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1000, + err: "", + }, + RunProgramTest { + prg: "(softfork (q . 919) (q 1 2 3) (q x) (q . ()))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 1000, + err: "softfork requires int arg", + }, + + // the cost value is checked in consensus mode as well + RunProgramTest { + prg: "(softfork (q . 1000))", + args: "()", + flags: 0, + result: None, + cost: 1000, + err: "cost exceeded", + }, + // the cost parameter is mandatory + RunProgramTest { + prg: "(softfork)", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "first of non-cons", + }, + RunProgramTest { + prg: "(softfork (q . 0))", + args: "()", + flags: 0, + result: None, + cost: 1000, + err: "cost must be > 0", + }, + // negative costs are not allowed + RunProgramTest { + prg: "(softfork (q . -1))", + args: "()", + flags: 0, + result: None, + cost: 1000, + err: "softfork requires positive int arg", + }, + RunProgramTest { + prg: "(softfork (q 1 2 3))", + args: "()", + flags: 0, + result: None, + cost: 1000, + err: "softfork requires int arg", + }, + + // test mismatching cost + RunProgramTest { + prg: "(softfork (q . 160) (q . 0) (q . (q . 42)) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 241, + err: "", + }, + // the program under the softfork is restricted by the specified cost + RunProgramTest { + prg: "(softfork (q . 159) (q . 0) (q . (q . 42)) (q . ()))", + args: "()", + flags: 0, + result: None, + cost: 241, + err: "cost exceeded", + }, + // the cost specified on the softfork must match exactly the cost of + // executing the program + RunProgramTest { + prg: "(softfork (q . 161) (q . 0) (q . (q . 42)) (q . ()))", + args: "()", + flags: 0, + result: None, + cost: 10000, + err: "softfork specified cost mismatch", + }, + + // without the flag to enable the BLS extensions, it's an unknown extension + RunProgramTest { + prg: "(softfork (q . 161) (q . 1) (q . (q . 42)) (q . ()))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 10000, + err: "unknown softfork extension", + }, + + // coinid extension + // make sure we can execute the coinid operator under softfork 0 + // this program raises an exception if the computed coin ID matches the + // expected + RunProgramTest { + prg: "(softfork (q . 1432) (q . 0) (q a (i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q x) (q . 0)) (q . ())) (q . ()))", + args: "()", + flags: 0, + result: None, + cost: 1513, + err: "clvm raise", + }, + // also test the opposite. This program is the same as above but it raises + // if the coin ID is a mismatch + RunProgramTest { + prg: "(softfork (q . 1432) (q . 0) (q a (i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x)) (q . ())) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1513, + err: "", + }, + + // coinid operator after hardfork, where coinid is available outside the + // softfork guard. + RunProgramTest { + prg: "(coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789))", + args: "()", + flags: ENABLE_BLS_OPS_OUTSIDE_GUARD, + result: Some("0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc"), + cost: 861, + err: "", + }, + RunProgramTest { + prg: "(coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 0x000123456789))", + args: "()", + flags: ENABLE_BLS_OPS_OUTSIDE_GUARD, + result: None, + cost: 861, + err: "coinid: invalid amount (may not have redundant leading zero)", + }, + // make sure the coinid operator is not available unless the flag is + // specified + RunProgramTest { + prg: "(coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 0x000123456789))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 861, + err: "unimplemented operator", + }, + + // secp261k1 + + RunProgramTest { + prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018))", + args: "()", + flags: 0, + result: Some("0"), + cost: 1300061, + err: "", + }, + // invalid signature + RunProgramTest { + prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c019))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "secp256k1_verify failed", + }, + + // secp261r1 + + RunProgramTest { + prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc))", + args: "()", + flags: 0, + result: Some("0"), + cost: 1850061, + err: "", + }, + // invalid signature + RunProgramTest { + prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffd))", + args: "()", + flags: 0, + result: None, + cost: 0, + err: "secp256r1_verify failed", + }, + ]; + + fn check(res: (NodePtr, &str)) -> NodePtr { + assert_eq!(res.1, ""); + res.0 + } + + fn run_test_case(t: &RunProgramTest) { + use crate::chia_dialect::ChiaDialect; + use crate::test_ops::node_eq; + let mut allocator = Allocator::new(); + + let program = check(parse_exp(&mut allocator, t.prg)); + let args = check(parse_exp(&mut allocator, t.args)); + let expected_result = &t.result.map(|v| check(parse_exp(&mut allocator, v))); + + let dialect = ChiaDialect::new(t.flags); + println!("prg: {}", t.prg); + match run_program(&mut allocator, &dialect, program, args, t.cost) { + Ok(Reduction(cost, prg_result)) => { + assert!(node_eq(&allocator, prg_result, expected_result.unwrap())); + assert_eq!(cost, t.cost); + + // now, run the same program again but with the cost limit 1 too low, to + // ensure it fails with the correct error + let expected_cost_exceeded = + run_program(&mut allocator, &dialect, program, args, t.cost - 1).unwrap_err(); + assert_eq!(expected_cost_exceeded.1, "cost exceeded"); + } + Err(err) => { + println!("FAILED: {}", err.1); + assert_eq!(err.1, t.err); + assert!(expected_result.is_none()); + } } } -} -#[test] -fn test_run_program() { - for t in TEST_CASES { - run_test_case(t); + #[test] + fn test_run_program() { + for t in TEST_CASES { + run_test_case(t); + } } -} -#[cfg(test)] -use rstest::rstest; - -// the test cases for this test consists of: -// prg: the program to run inside the softfork guard -// cost: the expected cost of the program (the test adds the apply-operator) -// enabled: the softfork extension number that enables operator in prg -// hard_fork_flag: the flag that enables the program to be run outside the guard -// err: the expected error message, empty string means OK -// The test programs are carefully crafted such that they fail with "clvm raise" -// when run in consensus mode and the operators are unknown. e.g. (coinid ...) -// returns NIL in that case, which compares not equal to the coin ID, which -// raises the exception. -// This property is relied on for the non-mempool and fork-not-activated cases. -#[cfg(test)] -#[rstest] -// make sure we can execute the coinid operator under softfork 0 -// this program raises an exception if the computed coin ID matches the -// expected -#[case::coinid("(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bd)) (q . 0) (q x))", + // the test cases for this test consists of: + // prg: the program to run inside the softfork guard + // cost: the expected cost of the program (the test adds the apply-operator) + // enabled: the softfork extension number that enables operator in prg + // hard_fork_flag: the flag that enables the program to be run outside the guard + // err: the expected error message, empty string means OK + // The test programs are carefully crafted such that they fail with "clvm raise" + // when run in consensus mode and the operators are unknown. e.g. (coinid ...) + // returns NIL in that case, which compares not equal to the coin ID, which + // raises the exception. + // This property is relied on for the non-mempool and fork-not-activated cases. + #[rstest] + // make sure we can execute the coinid operator under softfork 0 + // this program raises an exception if the computed coin ID matches the + // expected + #[case::coinid("(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bd)) (q . 0) (q x))", (1432, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "clvm raise")] -// also test the opposite. This program is the same as above but it raises -// if the coin ID is a mismatch -#[case::coinid("(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x))", + // also test the opposite. This program is the same as above but it raises + // if the coin ID is a mismatch + #[case::coinid("(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x))", (1432, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "")] -// modpow -#[case::modpow( + // modpow + #[case::modpow( "(i (= (modpow (q . 12345) (q . 6789) (q . 44444444444)) (q . 13456191581)) (q . 0) (q x))", (18241, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "" )] -#[case::modpow( + #[case::modpow( "(i (= (modpow (q . 12345) (q . 6789) (q . 44444444444)) (q . 13456191582)) (q . 0) (q x))", (18241, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "clvm raise" )] -// mod -#[case::modulus( + // mod + #[case::modulus( "(i (= (% (q . 80001) (q . 73)) (q . 66)) (q . 0) (q x))", (1564, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "" )] -#[case::modulus( + #[case::modulus( "(i (= (% (q . 80001) (q . 73)) (q . 67)) (q . 0) (q x))", (1564, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "clvm raise" )] -// g1_multiply -#[case::g1_mul("(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e)) (q . 0) (q x))", + // g1_multiply + #[case::g1_mul("(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e)) (q . 0) (q x))", (706634, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "")] -#[case::g1_mul( + #[case::g1_mul( "(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4f)) (q . 0) (q x))", (706634, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "clvm raise")] -#[case::g1_neg("(i (= (g1_negate (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", (706634, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "clvm raise")] -#[case::g1_neg("(i (= (g1_negate (q . 0xb2f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", + #[case::g1_neg("(i (= (g1_negate (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", (706634, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "clvm raise")] + #[case::g1_neg("(i (= (g1_negate (q . 0xb2f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", (706634, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "atom is not a valid G1 point")] -#[case::g2_add("(i (= (g2_add (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", + #[case::g2_add("(i (= (g2_add (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", (3981700, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "")] -#[case::g2_add("(i (= (g2_add (q . 0x93e12b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", + #[case::g2_add("(i (= (g2_add (q . 0x93e12b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", (3981700, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), "atom is not a G2 point")] -fn test_softfork( - #[case] prg: &'static str, - #[case] fields: (u64, u8, u32), // cost, enabled, hard_fork_flag - #[case] err: &'static str, - #[values(0)] flags: u32, - #[values(false, true)] mempool: bool, - #[values(0, 1, 2)] test_ext: u8, -) { - let (cost, enabled, hard_fork_flag) = fields; - let softfork_prg = - format!("(softfork (q . {cost}) (q . {test_ext}) (q . (a {prg} (q . 0))) (q . 0))"); - - let flags = flags | if mempool { NO_UNKNOWN_OPS } else { 0 }; - - // softfork extensions that are enabled - #[allow(clippy::match_like_matches_macro)] - let ext_enabled = match test_ext { - 0 => true, - _ => false, - }; - - println!("mempool: {mempool} ext: {test_ext} flags: {flags}"); - let expect_err = match (ext_enabled as u8, (test_ext >= enabled) as u8) { - // the extension we're running has not been activated, and we're not - // running an extension that supports the operator - (0, 0) => { - if mempool { - "unimplemented operator" - } else { - "" + fn test_softfork( + #[case] prg: &'static str, + #[case] fields: (u64, u8, u32), // cost, enabled, hard_fork_flag + #[case] err: &'static str, + #[values(0)] flags: u32, + #[values(false, true)] mempool: bool, + #[values(0, 1, 2)] test_ext: u8, + ) { + let (cost, enabled, hard_fork_flag) = fields; + let softfork_prg = + format!("(softfork (q . {cost}) (q . {test_ext}) (q . (a {prg} (q . 0))) (q . 0))"); + + let flags = flags | if mempool { NO_UNKNOWN_OPS } else { 0 }; + + // softfork extensions that are enabled + #[allow(clippy::match_like_matches_macro)] + let ext_enabled = match test_ext { + 0 => true, + _ => false, + }; + + println!("mempool: {mempool} ext: {test_ext} flags: {flags}"); + let expect_err = match (ext_enabled as u8, (test_ext >= enabled) as u8) { + // the extension we're running has not been activated, and we're not + // running an extension that supports the operator + (0, 0) => { + if mempool { + "unimplemented operator" + } else { + "" + } } - } - // the softfork extension hasn't been activated yet. It's a failure in - // mempool mode but ignored in consensus mode - (0, 1) => { - if mempool { - "unknown softfork extension" - } else { - "" + // the softfork extension hasn't been activated yet. It's a failure in + // mempool mode but ignored in consensus mode + (0, 1) => { + if mempool { + "unknown softfork extension" + } else { + "" + } } - } - // the extension we're invoking has been enabled, but the operator is - // not part of this extension. In mempool mode it's an error, in - // consensus mode the operator is considered unknown, returning - // NIL/false. This in turn will make the return value test fail, and - // raise an exception. - (1, 0) => { - if mempool { - "unimplemented operator" - } else { - "clvm raise" + // the extension we're invoking has been enabled, but the operator is + // not part of this extension. In mempool mode it's an error, in + // consensus mode the operator is considered unknown, returning + // NIL/false. This in turn will make the return value test fail, and + // raise an exception. + (1, 0) => { + if mempool { + "unimplemented operator" + } else { + "clvm raise" + } } - } - // the extension we're running has been activated, and we're running an - // extension the operator is available in. The program is executed and - // we get the expected result. - (1, 1) => err, - _ => unreachable!(), - }; - - println!("expect: {expect_err} cost: {cost}"); - let t = RunProgramTest { - prg: softfork_prg.as_str(), - args: "()", - flags, - result: if expect_err.is_empty() { - Some("()") - } else { - None - }, - cost: cost + 81, - err: expect_err, - }; + // the extension we're running has been activated, and we're running an + // extension the operator is available in. The program is executed and + // we get the expected result. + (1, 1) => err, + _ => unreachable!(), + }; - run_test_case(&t); + println!("expect: {expect_err} cost: {cost}"); + let t = RunProgramTest { + prg: softfork_prg.as_str(), + args: "()", + flags, + result: if expect_err.is_empty() { + Some("()") + } else { + None + }, + cost: cost + 81, + err: expect_err, + }; - // now test outside the guard (should fail unless hard_fork_flag is set). + run_test_case(&t); - let outside_guard_prg = format!("(a {prg} (q . 0))"); + // now test outside the guard (should fail unless hard_fork_flag is set). - // without the hard fork flag - println!("outside guard, no hard fork"); - let t = RunProgramTest { - prg: outside_guard_prg.as_str(), - args: "()", - flags, - result: None, - cost: cost - 140, - err: if mempool { - "unimplemented operator" - } else { - "clvm raise" - }, - }; - run_test_case(&t); - - // with the hard fork flag - println!("outside guard, hard fork activated"); - let t = RunProgramTest { - prg: outside_guard_prg.as_str(), - args: "()", - flags: flags | hard_fork_flag, - result: if err.is_empty() { Some("()") } else { None }, - cost: cost - 140, - err, - }; - run_test_case(&t); -} + let outside_guard_prg = format!("(a {prg} (q . 0))"); -#[cfg(feature = "counters")] -#[test] -fn test_counters() { - use crate::chia_dialect::ChiaDialect; + // without the hard fork flag + println!("outside guard, no hard fork"); + let t = RunProgramTest { + prg: outside_guard_prg.as_str(), + args: "()", + flags, + result: None, + cost: cost - 140, + err: if mempool { + "unimplemented operator" + } else { + "clvm raise" + }, + }; + run_test_case(&t); + + // with the hard fork flag + println!("outside guard, hard fork activated"); + let t = RunProgramTest { + prg: outside_guard_prg.as_str(), + args: "()", + flags: flags | hard_fork_flag, + result: if err.is_empty() { Some("()") } else { None }, + cost: cost - 140, + err, + }; + run_test_case(&t); + } + + #[cfg(feature = "counters")] + #[test] + fn test_counters() { + use crate::chia_dialect::ChiaDialect; - let mut a = Allocator::new(); + let mut a = Allocator::new(); - let program = check(parse_exp(&mut a, "(a (q 2 2 (c 2 (c 5 (c 11 ())))) (c (q 2 (i (= 11 ()) (q 1 . 1) (q 18 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ())))))) 1) 1))")); - let args = check(parse_exp(&mut a, "(5033 1000)")); - let cost = 15073165; + let program = check(parse_exp(&mut a, "(a (q 2 2 (c 2 (c 5 (c 11 ())))) (c (q 2 (i (= 11 ()) (q 1 . 1) (q 18 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ())))))) 1) 1))")); + let args = check(parse_exp(&mut a, "(5033 1000)")); + let cost = 15073165; - let (counters, result) = - run_program_with_counters(&mut a, &ChiaDialect::new(0), program, args, cost); + let (counters, result) = + run_program_with_counters(&mut a, &ChiaDialect::new(0), program, args, cost); - assert_eq!(counters.val_stack_usage, 3015); - assert_eq!(counters.env_stack_usage, 1005); - assert_eq!(counters.op_stack_usage, 3014); - assert_eq!(counters.atom_count, 998); - assert_eq!(counters.small_atom_count, 1042); - assert_eq!(counters.pair_count, 22077); - assert_eq!(counters.heap_size, 769963); + assert_eq!(counters.val_stack_usage, 3015); + assert_eq!(counters.env_stack_usage, 1005); + assert_eq!(counters.op_stack_usage, 3014); + assert_eq!(counters.atom_count, 998); + assert_eq!(counters.small_atom_count, 1042); + assert_eq!(counters.pair_count, 22077); + assert_eq!(counters.heap_size, 769963); - assert_eq!(result.unwrap().0, cost); + assert_eq!(result.unwrap().0, cost); + } } diff --git a/src/serde/de_br.rs b/src/serde/de_br.rs index b5282614..b1306aa0 100644 --- a/src/serde/de_br.rs +++ b/src/serde/de_br.rs @@ -80,105 +80,109 @@ pub fn node_from_bytes_backrefs_record( } #[cfg(test)] -use hex::FromHex; - -#[test] -fn test_deserialize_with_backrefs() { - fn deserialize_check(serialization_as_hex: &str, expected_hash_as_hex: &str) { - use crate::serde::object_cache::{treehash, ObjectCache}; - let buf = Vec::from_hex(serialization_as_hex).unwrap(); - let mut allocator = Allocator::new(); - let node = node_from_bytes_backrefs(&mut allocator, &buf).unwrap(); - - let mut oc = ObjectCache::new(&allocator, treehash); - let calculated_hash = oc.get_or_calculate(&node).unwrap(); - let ch: &[u8] = calculated_hash; - let expected_hash: Vec = Vec::from_hex(expected_hash_as_hex).unwrap(); - assert_eq!(expected_hash, ch); - } +mod tests { + use super::*; + + use hex::FromHex; + + #[test] + fn test_deserialize_with_backrefs() { + fn deserialize_check(serialization_as_hex: &str, expected_hash_as_hex: &str) { + use crate::serde::object_cache::{treehash, ObjectCache}; + let buf = Vec::from_hex(serialization_as_hex).unwrap(); + let mut allocator = Allocator::new(); + let node = node_from_bytes_backrefs(&mut allocator, &buf).unwrap(); + + let mut oc = ObjectCache::new(&allocator, treehash); + let calculated_hash = oc.get_or_calculate(&node).unwrap(); + let ch: &[u8] = calculated_hash; + let expected_hash: Vec = Vec::from_hex(expected_hash_as_hex).unwrap(); + assert_eq!(expected_hash, ch); + } - // ("foobar" "foobar") - deserialize_check( - "ff86666f6f626172ff86666f6f62617280", - "9148834131750904c023598bed28db269bdb29012514579e723d63e27829bcba", - ); - deserialize_check( - "ff86666f6f626172fe01", // ("foobar" "foobar") - "9148834131750904c023598bed28db269bdb29012514579e723d63e27829bcba", - ); + // ("foobar" "foobar") + deserialize_check( + "ff86666f6f626172ff86666f6f62617280", + "9148834131750904c023598bed28db269bdb29012514579e723d63e27829bcba", + ); + deserialize_check( + "ff86666f6f626172fe01", // ("foobar" "foobar") + "9148834131750904c023598bed28db269bdb29012514579e723d63e27829bcba", + ); - // ((1 2 3 4) 1 2 3 4) - deserialize_check( - "ffff01ff02ff03ff0480ff01ff02ff03ff0480", - "028c16eb4fec600e6153d8dde60eb3916d13d0dc446b5cd7936a1248f8963bf8", - ); - deserialize_check( - "ffff01ff02ff03ff0480fe02", // ((1 2 3 4) 1 2 3 4) - "028c16eb4fec600e6153d8dde60eb3916d13d0dc446b5cd7936a1248f8963bf8", - ); + // ((1 2 3 4) 1 2 3 4) + deserialize_check( + "ffff01ff02ff03ff0480ff01ff02ff03ff0480", + "028c16eb4fec600e6153d8dde60eb3916d13d0dc446b5cd7936a1248f8963bf8", + ); + deserialize_check( + "ffff01ff02ff03ff0480fe02", // ((1 2 3 4) 1 2 3 4) + "028c16eb4fec600e6153d8dde60eb3916d13d0dc446b5cd7936a1248f8963bf8", + ); - // `(((((a_very_long_repeated_string . 1) . (2 . 3)) . ((4 . 5) . (6 . 7))) . (8 . 9)) 10 a_very_long_repeated_string)` - deserialize_check( - "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\ + // `(((((a_very_long_repeated_string . 1) . (2 . 3)) . ((4 . 5) . (6 . 7))) . (8 . 9)) 10 a_very_long_repeated_string)` + deserialize_check( + "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\ 05ff0607ff0809ff0aff9b615f766572795f6c6f6e675f72657065617465645f737472696e6780", - "e23c73777f814e8a4e2785487b272b8b22ddaded1f7cfb808b43f1148602882f", - ); - deserialize_check( + "e23c73777f814e8a4e2785487b272b8b22ddaded1f7cfb808b43f1148602882f", + ); + deserialize_check( "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff0405ff0607ff0809ff0afffe4180", "e23c73777f814e8a4e2785487b272b8b22ddaded1f7cfb808b43f1148602882f", ); -} - -#[test] -fn test_deserialize_with_backrefs_record() { - fn deserialize_check(serialization_as_hex: &str, expected_backrefs: &[&'static str]) { - use crate::serde::node_to_bytes; - let buf = Vec::from_hex(serialization_as_hex).unwrap(); - let mut allocator = Allocator::new(); - let (_node, backrefs) = node_from_bytes_backrefs_record(&mut allocator, &buf) - .expect("node_from_bytes_backrefs_records"); - println!("backrefs: {:?}", backrefs); - assert_eq!(backrefs.len(), expected_backrefs.len()); - - let expected_backrefs = - HashSet::::from_iter(expected_backrefs.iter().map(|s| s.to_string())); - let backrefs = HashSet::from_iter( - backrefs - .iter() - .map(|br| hex::encode(node_to_bytes(&allocator, *br).expect("node_to_bytes"))), - ); - - assert_eq!(backrefs, expected_backrefs); } - // ("foobar" "foobar") - // no-backrefs - deserialize_check("ff86666f6f626172ff86666f6f62617280", &[]); - // with back-refs - deserialize_check( - "ff86666f6f626172fe01", // ("foobar" "foobar") - &["ff86666f6f62617280"], - ); + #[test] + fn test_deserialize_with_backrefs_record() { + fn deserialize_check(serialization_as_hex: &str, expected_backrefs: &[&'static str]) { + use crate::serde::node_to_bytes; + let buf = Vec::from_hex(serialization_as_hex).unwrap(); + let mut allocator = Allocator::new(); + let (_node, backrefs) = node_from_bytes_backrefs_record(&mut allocator, &buf) + .expect("node_from_bytes_backrefs_records"); + println!("backrefs: {:?}", backrefs); + assert_eq!(backrefs.len(), expected_backrefs.len()); + + let expected_backrefs = + HashSet::::from_iter(expected_backrefs.iter().map(|s| s.to_string())); + let backrefs = HashSet::from_iter( + backrefs + .iter() + .map(|br| hex::encode(node_to_bytes(&allocator, *br).expect("node_to_bytes"))), + ); + + assert_eq!(backrefs, expected_backrefs); + } - // ((1 2 3 4) 1 2 3 4) - // no-backrefs - deserialize_check("ffff01ff02ff03ff0480ff01ff02ff03ff0480", &[]); - // with back-refs - deserialize_check( - "ffff01ff02ff03ff0480fe02", // ((1 2 3 4) 1 2 3 4) - &["ff01ff02ff03ff0480"], - ); + // ("foobar" "foobar") + // no-backrefs + deserialize_check("ff86666f6f626172ff86666f6f62617280", &[]); + // with back-refs + deserialize_check( + "ff86666f6f626172fe01", // ("foobar" "foobar") + &["ff86666f6f62617280"], + ); + + // ((1 2 3 4) 1 2 3 4) + // no-backrefs + deserialize_check("ffff01ff02ff03ff0480ff01ff02ff03ff0480", &[]); + // with back-refs + deserialize_check( + "ffff01ff02ff03ff0480fe02", // ((1 2 3 4) 1 2 3 4) + &["ff01ff02ff03ff0480"], + ); - // `(((((a_very_long_repeated_string . 1) . (2 . 3)) . ((4 . 5) . (6 . 7))) . (8 . 9)) 10 a_very_long_repeated_string)` - // no-backrefs - deserialize_check( - "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\ + // `(((((a_very_long_repeated_string . 1) . (2 . 3)) . ((4 . 5) . (6 . 7))) . (8 . 9)) 10 a_very_long_repeated_string)` + // no-backrefs + deserialize_check( + "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\ 05ff0607ff0809ff0aff9b615f766572795f6c6f6e675f72657065617465645f737472696e6780", - &[], - ); - // with back-refs - deserialize_check( + &[], + ); + // with back-refs + deserialize_check( "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff0405ff0607ff0809ff0afffe4180", &["9b615f766572795f6c6f6e675f72657065617465645f737472696e67"], ); + } } diff --git a/src/serde/de_tree.rs b/src/serde/de_tree.rs index 6c595bfc..d645df09 100644 --- a/src/serde/de_tree.rs +++ b/src/serde/de_tree.rs @@ -221,136 +221,136 @@ pub fn parse_triples( } #[cfg(test)] -use std::io::Cursor; +mod tests { + use super::*; -#[cfg(test)] -use hex::FromHex; - -#[cfg(test)] -fn check_parse_tree(h: &str, expected: Vec, expected_sha_tree_hex: &str) { - let b = Vec::from_hex(h).unwrap(); - println!("{:?}", b); - let mut f = Cursor::new(b); - let (p, tree_hash) = parse_triples(&mut f, false).unwrap(); - assert_eq!(p, expected); - assert_eq!(tree_hash, None); + use hex::FromHex; + use std::io::Cursor; - let b = Vec::from_hex(h).unwrap(); - let mut f = Cursor::new(b); - let (p, tree_hash) = parse_triples(&mut f, true).unwrap(); - assert_eq!(p, expected); + fn check_parse_tree(h: &str, expected: Vec, expected_sha_tree_hex: &str) { + let b = Vec::from_hex(h).unwrap(); + println!("{:?}", b); + let mut f = Cursor::new(b); + let (p, tree_hash) = parse_triples(&mut f, false).unwrap(); + assert_eq!(p, expected); + assert_eq!(tree_hash, None); - let est = Vec::from_hex(expected_sha_tree_hex).unwrap(); - assert_eq!(tree_hash.unwrap()[0].to_vec(), est); -} + let b = Vec::from_hex(h).unwrap(); + let mut f = Cursor::new(b); + let (p, tree_hash) = parse_triples(&mut f, true).unwrap(); + assert_eq!(p, expected); -#[cfg(test)] -fn check_sha_blobs(h: &str, blobs: &[&[u8]]) { - let exp_sha = Vec::from_hex(h).unwrap(); - let actual_sha = sha_blobs(blobs); - assert_eq!(exp_sha, actual_sha); -} + let est = Vec::from_hex(expected_sha_tree_hex).unwrap(); + assert_eq!(tree_hash.unwrap()[0].to_vec(), est); + } -#[test] -fn test_sha_blobs() { - check_sha_blobs( - "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", - &[&[1_u8]], - ); - check_sha_blobs( - "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2", - &[&[1], &[1]], - ); - check_sha_blobs( - "812195e02ed84360ceafab26f9fa6072f8aa76ba34a735894c3f3c2e4fe6911d", - &[&[1, 250, 17], &[28]], - ); -} + fn check_sha_blobs(h: &str, blobs: &[&[u8]]) { + let exp_sha = Vec::from_hex(h).unwrap(); + let actual_sha = sha_blobs(blobs); + assert_eq!(exp_sha, actual_sha); + } -#[test] -fn test_parse_tree() { - check_parse_tree( - "80", - vec![ParsedTriple::Atom { - start: 0, - end: 1, - atom_offset: 1, - }], - "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", - ); + #[test] + fn test_sha_blobs() { + check_sha_blobs( + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + &[&[1_u8]], + ); + check_sha_blobs( + "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2", + &[&[1], &[1]], + ); + check_sha_blobs( + "812195e02ed84360ceafab26f9fa6072f8aa76ba34a735894c3f3c2e4fe6911d", + &[&[1, 250, 17], &[28]], + ); + } - check_parse_tree( - "ff648200c8", - vec![ - ParsedTriple::Pair { + #[test] + fn test_parse_tree() { + check_parse_tree( + "80", + vec![ParsedTriple::Atom { start: 0, - end: 5, - right_index: 2, - }, - ParsedTriple::Atom { - start: 1, - end: 2, - atom_offset: 0, - }, - ParsedTriple::Atom { - start: 2, - end: 5, + end: 1, atom_offset: 1, - }, - ], - "247f7d3f63b346ea93ca47f571cd0f4455392348b888a4286072bef0ac6069b5", - ); + }], + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + ); - check_parse_tree( - "ff83666f6fff83626172ff8362617a80", // `(foo bar baz)` - vec![ - ParsedTriple::Pair { - start: 0, - end: 16, - right_index: 2, - }, - ParsedTriple::Atom { - start: 1, - end: 5, - atom_offset: 1, - }, - ParsedTriple::Pair { - start: 5, - end: 16, - right_index: 4, - }, - ParsedTriple::Atom { - start: 6, - end: 10, - atom_offset: 1, - }, - ParsedTriple::Pair { - start: 10, - end: 16, - right_index: 6, - }, - ParsedTriple::Atom { - start: 11, - end: 15, - atom_offset: 1, - }, - ParsedTriple::Atom { - start: 15, - end: 16, - atom_offset: 1, - }, - ], - "47f30bf9935e25e4262023124fb5e986d755b9ed65a28ac78925c933bfd57dbd", - ); + check_parse_tree( + "ff648200c8", + vec![ + ParsedTriple::Pair { + start: 0, + end: 5, + right_index: 2, + }, + ParsedTriple::Atom { + start: 1, + end: 2, + atom_offset: 0, + }, + ParsedTriple::Atom { + start: 2, + end: 5, + atom_offset: 1, + }, + ], + "247f7d3f63b346ea93ca47f571cd0f4455392348b888a4286072bef0ac6069b5", + ); + + check_parse_tree( + "ff83666f6fff83626172ff8362617a80", // `(foo bar baz)` + vec![ + ParsedTriple::Pair { + start: 0, + end: 16, + right_index: 2, + }, + ParsedTriple::Atom { + start: 1, + end: 5, + atom_offset: 1, + }, + ParsedTriple::Pair { + start: 5, + end: 16, + right_index: 4, + }, + ParsedTriple::Atom { + start: 6, + end: 10, + atom_offset: 1, + }, + ParsedTriple::Pair { + start: 10, + end: 16, + right_index: 6, + }, + ParsedTriple::Atom { + start: 11, + end: 15, + atom_offset: 1, + }, + ParsedTriple::Atom { + start: 15, + end: 16, + atom_offset: 1, + }, + ], + "47f30bf9935e25e4262023124fb5e986d755b9ed65a28ac78925c933bfd57dbd", + ); - let s = "c0a0".to_owned() + &hex::encode([0x31u8; 160]); - check_parse_tree( - &s, - vec![ParsedTriple::Atom { - start: 0, - end: 162, - atom_offset: 2, - }], - "d1c109981a9c5a3bbe2d98795a186a0f057dc9a3a7f5e1eb4dfb63a1636efa2d", - ); + let s = "c0a0".to_owned() + &hex::encode([0x31u8; 160]); + check_parse_tree( + &s, + vec![ParsedTriple::Atom { + start: 0, + end: 162, + atom_offset: 2, + }], + "d1c109981a9c5a3bbe2d98795a186a0f057dc9a3a7f5e1eb4dfb63a1636efa2d", + ); + } } diff --git a/src/serde/object_cache.rs b/src/serde/object_cache.rs index a70b5348..4df080b1 100644 --- a/src/serde/object_cache.rs +++ b/src/serde/object_cache.rs @@ -133,129 +133,124 @@ pub fn serialized_length( } #[cfg(test)] -use std::cmp::max; - -#[cfg(test)] -use std::fmt::Debug; - -#[cfg(test)] -use std::io::Cursor; - -#[cfg(test)] -use hex::FromHex; - -#[cfg(test)] -use crate::serde::de::node_from_stream; - -/// calculate the depth of a node. Used for tests - -#[cfg(test)] -fn calculate_depth_simple( - cache: &mut ObjectCache, - allocator: &Allocator, - node: NodePtr, -) -> Option { - match allocator.sexp(node) { - SExp::Pair(left, right) => match cache.get_from_cache(&left) { - None => None, - Some(left_value) => cache - .get_from_cache(&right) - .map(|right_value| 1 + max(*left_value, *right_value)), - }, - SExp::Atom => Some(0), +mod tests { + use super::*; + + use hex::FromHex; + use std::cmp::max; + use std::fmt::Debug; + use std::io::Cursor; + + use crate::serde::de::node_from_stream; + + /// calculate the depth of a node. Used for tests + fn calculate_depth_simple( + cache: &mut ObjectCache, + allocator: &Allocator, + node: NodePtr, + ) -> Option { + match allocator.sexp(node) { + SExp::Pair(left, right) => match cache.get_from_cache(&left) { + None => None, + Some(left_value) => cache + .get_from_cache(&right) + .map(|right_value| 1 + max(*left_value, *right_value)), + }, + SExp::Atom => Some(0), + } } -} -#[cfg(test)] -fn check_cached_function(obj_as_hex: &str, expected_value: T, f: CachedFunction) -where - T: Clone + Eq + Debug, -{ - let mut allocator = Allocator::new(); - let blob: Vec = Vec::from_hex(obj_as_hex).unwrap(); - let mut cursor: Cursor<&[u8]> = Cursor::new(&blob); - let obj = node_from_stream(&mut allocator, &mut cursor).unwrap(); - let mut oc = ObjectCache::new(&allocator, f); + fn check_cached_function(obj_as_hex: &str, expected_value: T, f: CachedFunction) + where + T: Clone + Eq + Debug, + { + let mut allocator = Allocator::new(); + let blob: Vec = Vec::from_hex(obj_as_hex).unwrap(); + let mut cursor: Cursor<&[u8]> = Cursor::new(&blob); + let obj = node_from_stream(&mut allocator, &mut cursor).unwrap(); + let mut oc = ObjectCache::new(&allocator, f); - assert_eq!(oc.get_from_cache(&obj), None); + assert_eq!(oc.get_from_cache(&obj), None); - oc.calculate(&obj); + oc.calculate(&obj); - assert_eq!(oc.get_from_cache(&obj), Some(&expected_value)); + assert_eq!(oc.get_from_cache(&obj), Some(&expected_value)); - assert_eq!(oc.get_or_calculate(&obj).unwrap().clone(), expected_value); + assert_eq!(oc.get_or_calculate(&obj).unwrap().clone(), expected_value); - assert_eq!(oc.get_from_cache(&obj), Some(&expected_value)); + assert_eq!(oc.get_from_cache(&obj), Some(&expected_value)); - // do it again, but the simple way - let mut oc = ObjectCache::new(&allocator, f); - assert_eq!(oc.get_or_calculate(&obj).unwrap().clone(), expected_value); -} + // do it again, but the simple way + let mut oc = ObjectCache::new(&allocator, f); + assert_eq!(oc.get_or_calculate(&obj).unwrap().clone(), expected_value); + } -#[test] -fn test_depths_cache() { - let check = |a, b| check_cached_function(a, b, calculate_depth_simple); - check("01", 0); // 1 - check("ff83666f6f83626172", 1); // (foo . bar) - check("ff83666f6fff8362617280", 2); // (foo bar) - check("ffff0102ff0304", 2); // ((1 . 2) . (3 . 4)) - check("ff01ff02ff03ff04ff05ff0680", 6); // (1 2 3 4 5 6) -} + #[test] + fn test_depths_cache() { + let check = |a, b| check_cached_function(a, b, calculate_depth_simple); + check("01", 0); // 1 + check("ff83666f6f83626172", 1); // (foo . bar) + check("ff83666f6fff8362617280", 2); // (foo bar) + check("ffff0102ff0304", 2); // ((1 . 2) . (3 . 4)) + check("ff01ff02ff03ff04ff05ff0680", 6); // (1 2 3 4 5 6) + } -#[test] -fn test_treehash() { - let check = |a, b| check_cached_function(a, Bytes32::from_hex(b).unwrap(), treehash); - check( - "ff83666f6f83626172", - "c518e45ae6a7b4146017b7a1d81639051b132f1f5572ce3088a3898a9ed1280b", - ); // (foo . bar) - check( - "ff83666f6fff8362617280", - "c97d97cc81100a4980080ba81ff1ba3985f7cff1db9d41d904b9d512bb875144", - ); // (foo bar) - check( - "ffff0102ff0304", - "2824018d148bc6aed0847e2c86aaa8a5407b916169f15b12cea31fa932fc4c8d", - ); // ((1 . 2) . (3 . 4)) - check( - "ff01ff02ff03ff04ff05ff0680", - "65de5098d18bebd62aee37de32f0b62d1803d9c7c48f10dca25501243d7a0392", - ); // (1 2 3 4 5 6) -} + #[test] + fn test_treehash() { + let check = |a, b| check_cached_function(a, Bytes32::from_hex(b).unwrap(), treehash); + check( + "ff83666f6f83626172", + "c518e45ae6a7b4146017b7a1d81639051b132f1f5572ce3088a3898a9ed1280b", + ); // (foo . bar) + check( + "ff83666f6fff8362617280", + "c97d97cc81100a4980080ba81ff1ba3985f7cff1db9d41d904b9d512bb875144", + ); // (foo bar) + check( + "ffff0102ff0304", + "2824018d148bc6aed0847e2c86aaa8a5407b916169f15b12cea31fa932fc4c8d", + ); // ((1 . 2) . (3 . 4)) + check( + "ff01ff02ff03ff04ff05ff0680", + "65de5098d18bebd62aee37de32f0b62d1803d9c7c48f10dca25501243d7a0392", + ); // (1 2 3 4 5 6) + } -#[test] -fn test_serialized_length() { - let check = |a, b| check_cached_function(a, b, serialized_length); - check("ff83666f6f83626172", 9); // (foo . bar) - check("ff83666f6fff8362617280", 11); // (foo bar) - check("ffff0102ff0304", 7); // ((1 . 2) . (3 . 4)) - check("ff01ff02ff03ff04ff05ff0680", 13); // (1 2 3 4 5 6) -} + #[test] + fn test_serialized_length() { + let check = |a, b| check_cached_function(a, b, serialized_length); + check("ff83666f6f83626172", 9); // (foo . bar) + check("ff83666f6fff8362617280", 11); // (foo bar) + check("ffff0102ff0304", 7); // ((1 . 2) . (3 . 4)) + check("ff01ff02ff03ff04ff05ff0680", 13); // (1 2 3 4 5 6) + } -// this test takes a very long time (>60s) in debug mode, so it only runs in release mode + // this test takes a very long time (>60s) in debug mode, so it only runs in release mode + + #[cfg(not(debug_assertions))] + #[test] + fn test_very_long_list() { + // in this test, we check that `treehash` and `serialized_length` can handle very deep trees that + // would normally blow out the stack. It's expensive to create such a long list, so we do both + // tests here so we only have to to create the list once + + const LIST_SIZE: u64 = 20_000_000; + let mut allocator = Allocator::new(); + let mut top = allocator.nil(); + for _ in 0..LIST_SIZE { + let atom = allocator.one(); + top = allocator.new_pair(atom, top).unwrap(); + } -#[cfg(not(debug_assertions))] -#[test] -fn test_very_long_list() { - // in this test, we check that `treehash` and `serialized_length` can handle very deep trees that - // would normally blow out the stack. It's expensive to create such a long list, so we do both - // tests here so we only have to to create the list once + let expected_value = LIST_SIZE * 2 + 1; + let mut oc = ObjectCache::new(&allocator, serialized_length); + assert_eq!(oc.get_or_calculate(&top).unwrap().clone(), expected_value); - const LIST_SIZE: u64 = 20_000_000; - let mut allocator = Allocator::new(); - let mut top = allocator.nil(); - for _ in 0..LIST_SIZE { - let atom = allocator.one(); - top = allocator.new_pair(atom, top).unwrap(); + let expected_value = <[u8; 32]>::from_hex( + "a168fce695099a30c0745075e6db3722ed7f059e0d7cc4d7e7504e215db5017b", + ) + .unwrap(); + let mut oc = ObjectCache::new(&allocator, treehash); + assert_eq!(oc.get_or_calculate(&top).unwrap().clone(), expected_value); } - - let expected_value = LIST_SIZE * 2 + 1; - let mut oc = ObjectCache::new(&allocator, serialized_length); - assert_eq!(oc.get_or_calculate(&top).unwrap().clone(), expected_value); - - let expected_value = - <[u8; 32]>::from_hex("a168fce695099a30c0745075e6db3722ed7f059e0d7cc4d7e7504e215db5017b") - .unwrap(); - let mut oc = ObjectCache::new(&allocator, treehash); - assert_eq!(oc.get_or_calculate(&top).unwrap().clone(), expected_value); } diff --git a/src/serde/parse_atom.rs b/src/serde/parse_atom.rs index 5bdd866a..ce5c5990 100644 --- a/src/serde/parse_atom.rs +++ b/src/serde/parse_atom.rs @@ -52,7 +52,6 @@ pub fn decode_size(f: &mut R, initial_b: u8) -> Result { /// parse an atom from the stream and return a pointer to it /// the first byte has already been read - fn parse_atom_ptr<'a>(f: &'a mut Cursor<&[u8]>, first_byte: u8) -> Result<&'a [u8]> { let blob = if first_byte <= MAX_SINGLE_BYTE { let pos = f.position() as usize; @@ -73,7 +72,6 @@ fn parse_atom_ptr<'a>(f: &'a mut Cursor<&[u8]>, first_byte: u8) -> Result<&'a [u /// At this point, the first byte has already been read to ensure it's /// not a special code like `CONS_BOX_MARKER` = 0xff, so it must be /// passed in too - pub fn parse_atom( allocator: &mut Allocator, first_byte: u8, @@ -90,7 +88,6 @@ pub fn parse_atom( } /// parse an atom from the stream and return a pointer to it - pub fn parse_path<'a>(f: &'a mut Cursor<&[u8]>) -> Result<&'a [u8]> { let mut buf1: [u8; 1] = [0]; f.read_exact(&mut buf1)?; @@ -98,131 +95,132 @@ pub fn parse_path<'a>(f: &'a mut Cursor<&[u8]>) -> Result<&'a [u8]> { } #[cfg(test)] -use std::io::ErrorKind; - -#[cfg(test)] -use super::write_atom::write_atom; - -#[test] -fn test_decode_size() { - // single-byte length prefix - let mut buffer = Cursor::new(&[]); - assert_eq!( - decode_size_with_offset(&mut buffer, 0x80 | 0x20).unwrap(), - (1, 0x20) - ); - - // two-byte length prefix - let first = 0b11001111; - let mut buffer = Cursor::new(&[0xaa]); - assert_eq!( - decode_size_with_offset(&mut buffer, first).unwrap(), - (2, 0xfaa) - ); -} - -#[test] -fn test_large_decode_size() { - // this is an atom length-prefix 0xffffffffffff, or (2^48 - 1). - // We don't support atoms this large and we should fail before attempting to - // allocate this much memory - let first = 0b11111110; - let mut buffer = Cursor::new(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); - let ret = decode_size_with_offset(&mut buffer, first); - let e = ret.unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - - // this is still too large - let first = 0b11111100; - let mut buffer = Cursor::new(&[0x4, 0, 0, 0, 0]); - let ret = decode_size_with_offset(&mut buffer, first); - let e = ret.unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - - // But this is *just* within what we support - // Still a very large blob, probably enough for a DoS attack - let first = 0b11111100; - let mut buffer = Cursor::new(&[0x3, 0xff, 0xff, 0xff, 0xff]); - assert_eq!( - decode_size_with_offset(&mut buffer, first).unwrap(), - (6, 0x3ffffffff) - ); - - // this ensures a fuzzer-found bug doesn't reoccur - let mut buffer = Cursor::new(&[0xff, 0xfe]); - let ret = decode_size_with_offset(&mut buffer, first); - let e = ret.unwrap_err(); - assert_eq!(e.kind(), ErrorKind::UnexpectedEof); - assert_eq!(e.to_string(), "failed to fill whole buffer"); -} +mod tests { + use crate::serde::write_atom::write_atom; + + use super::*; + + use std::io::ErrorKind; + + #[test] + fn test_decode_size() { + // single-byte length prefix + let mut buffer = Cursor::new(&[]); + assert_eq!( + decode_size_with_offset(&mut buffer, 0x80 | 0x20).unwrap(), + (1, 0x20) + ); + + // two-byte length prefix + let first = 0b11001111; + let mut buffer = Cursor::new(&[0xaa]); + assert_eq!( + decode_size_with_offset(&mut buffer, first).unwrap(), + (2, 0xfaa) + ); + } -#[test] -fn test_truncated_decode_size() { - // the stream is truncated - let first = 0b11111100; - let mut cursor = Cursor::new(&[0x4, 0, 0, 0]); - let ret = decode_size_with_offset(&mut cursor, first); - let e = ret.unwrap_err(); - assert_eq!(e.kind(), ErrorKind::UnexpectedEof); -} + #[test] + fn test_large_decode_size() { + // this is an atom length-prefix 0xffffffffffff, or (2^48 - 1). + // We don't support atoms this large and we should fail before attempting to + // allocate this much memory + let first = 0b11111110; + let mut buffer = Cursor::new(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + let ret = decode_size_with_offset(&mut buffer, first); + let e = ret.unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + // this is still too large + let first = 0b11111100; + let mut buffer = Cursor::new(&[0x4, 0, 0, 0, 0]); + let ret = decode_size_with_offset(&mut buffer, first); + let e = ret.unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + // But this is *just* within what we support + // Still a very large blob, probably enough for a DoS attack + let first = 0b11111100; + let mut buffer = Cursor::new(&[0x3, 0xff, 0xff, 0xff, 0xff]); + assert_eq!( + decode_size_with_offset(&mut buffer, first).unwrap(), + (6, 0x3ffffffff) + ); + + // this ensures a fuzzer-found bug doesn't reoccur + let mut buffer = Cursor::new(&[0xff, 0xfe]); + let ret = decode_size_with_offset(&mut buffer, first); + let e = ret.unwrap_err(); + assert_eq!(e.kind(), ErrorKind::UnexpectedEof); + assert_eq!(e.to_string(), "failed to fill whole buffer"); + } -#[cfg(test)] -fn check_parse_atom(blob: &[u8], expected_atom: &[u8]) { - let mut cursor = Cursor::<&[u8]>::new(blob); - let mut first: [u8; 1] = [0]; - cursor.read_exact(&mut first).unwrap(); - let first = first[0]; + #[test] + fn test_truncated_decode_size() { + // the stream is truncated + let first = 0b11111100; + let mut cursor = Cursor::new(&[0x4, 0, 0, 0]); + let ret = decode_size_with_offset(&mut cursor, first); + let e = ret.unwrap_err(); + assert_eq!(e.kind(), ErrorKind::UnexpectedEof); + } - let mut allocator = Allocator::new(); - let atom_node = parse_atom(&mut allocator, first, &mut cursor).unwrap(); + fn check_parse_atom(blob: &[u8], expected_atom: &[u8]) { + let mut cursor = Cursor::<&[u8]>::new(blob); + let mut first: [u8; 1] = [0]; + cursor.read_exact(&mut first).unwrap(); + let first = first[0]; - let atom = allocator.atom(atom_node); + let mut allocator = Allocator::new(); + let atom_node = parse_atom(&mut allocator, first, &mut cursor).unwrap(); - assert_eq!(expected_atom, atom.as_ref()); -} + let atom = allocator.atom(atom_node); -#[cfg(test)] -fn check_parse_atom_str(blob_hex: &str, expected_atom_hex: &str) { - let blob = hex::decode(blob_hex).unwrap(); - let expected_atom: &[u8] = &hex::decode(expected_atom_hex).unwrap(); - check_parse_atom(&blob, expected_atom); -} + assert_eq!(expected_atom, atom.as_ref()); + } -#[test] -fn test_parse_atom() { - check_parse_atom_str("80", ""); - // try "00", "01", "02", ..., "7f" - for idx in 0..128 { - check_parse_atom(&[idx], &[idx]); + fn check_parse_atom_str(blob_hex: &str, expected_atom_hex: &str) { + let blob = hex::decode(blob_hex).unwrap(); + let expected_atom: &[u8] = &hex::decode(expected_atom_hex).unwrap(); + check_parse_atom(&blob, expected_atom); } - // check a short atom - check_parse_atom_str("83666f6f", "666f6f"); - - // check long atoms near boundary conditions - let n = 3; - let base_lengths = [0, 0x40 - n, 0x2000 - n, 0x100000 - n, 0x08000000 - n]; - let mut atom_vec = vec![]; - for base_length in base_lengths.iter() { - for size_offset in 0..6 { - let size = base_length + size_offset; - atom_vec.resize(size, 0x66); - let mut buffer: Vec = vec![]; - let mut cursor = Cursor::new(&mut buffer); - write_atom(&mut cursor, &atom_vec).unwrap(); + #[test] + fn test_parse_atom() { + check_parse_atom_str("80", ""); + // try "00", "01", "02", ..., "7f" + for idx in 0..128 { + check_parse_atom(&[idx], &[idx]); + } + + // check a short atom + check_parse_atom_str("83666f6f", "666f6f"); + + // check long atoms near boundary conditions + let n = 3; + let base_lengths = [0, 0x40 - n, 0x2000 - n, 0x100000 - n, 0x08000000 - n]; + let mut atom_vec = vec![]; + for base_length in base_lengths.iter() { + for size_offset in 0..6 { + let size = base_length + size_offset; + atom_vec.resize(size, 0x66); + let mut buffer: Vec = vec![]; + let mut cursor = Cursor::new(&mut buffer); + write_atom(&mut cursor, &atom_vec).unwrap(); + } } } -} -#[test] -fn test_truncated_parse_atom() { - // the stream is truncated - let first = 0b11111100; - let mut cursor = Cursor::<&[u8]>::new(&[0x4, 0, 0, 0]); - let mut allocator = Allocator::new(); - let ret = parse_atom(&mut allocator, first, &mut cursor); - let err = ret.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::UnexpectedEof); + #[test] + fn test_truncated_parse_atom() { + // the stream is truncated + let first = 0b11111100; + let mut cursor = Cursor::<&[u8]>::new(&[0x4, 0, 0, 0]); + let mut allocator = Allocator::new(); + let ret = parse_atom(&mut allocator, first, &mut cursor); + let err = ret.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::UnexpectedEof); + } } diff --git a/src/serde/read_cache_lookup.rs b/src/serde/read_cache_lookup.rs index 37e79261..d01e2b1d 100644 --- a/src/serde/read_cache_lookup.rs +++ b/src/serde/read_cache_lookup.rs @@ -185,7 +185,6 @@ impl ReadCacheLookup { /// If `A` => `v` then `[A] + [0]` => `v * 2` and `[A] + [1]` => `v * 2 + 1` /// Then the integer is turned into the minimal-length array of `u8` representing /// that value as an unsigned integer. - fn reversed_path_to_vec_u8(path: &[u8]) -> Vec { let byte_count = (path.len() + 1 + 7) >> 3; let mut v = vec![0; byte_count]; @@ -208,164 +207,169 @@ fn reversed_path_to_vec_u8(path: &[u8]) -> Vec { v } -#[test] -fn test_path_to_vec_u8() { - assert_eq!(reversed_path_to_vec_u8(&[]), vec!(0b1)); - assert_eq!(reversed_path_to_vec_u8(&[0]), vec!(0b10)); - assert_eq!(reversed_path_to_vec_u8(&[1]), vec!(0b11)); - assert_eq!(reversed_path_to_vec_u8(&[0, 0]), vec!(0b100)); - assert_eq!(reversed_path_to_vec_u8(&[0, 1]), vec!(0b101)); - assert_eq!(reversed_path_to_vec_u8(&[1, 0]), vec!(0b110)); - assert_eq!(reversed_path_to_vec_u8(&[1, 1]), vec!(0b111)); - assert_eq!(reversed_path_to_vec_u8(&[1, 1, 1]), vec!(0b1111)); - assert_eq!(reversed_path_to_vec_u8(&[0, 1, 1, 1]), vec!(0b10111)); - assert_eq!(reversed_path_to_vec_u8(&[1, 0, 1, 1, 1]), vec!(0b110111)); - assert_eq!( - reversed_path_to_vec_u8(&[1, 1, 0, 1, 1, 1]), - vec!(0b1110111) - ); - assert_eq!( - reversed_path_to_vec_u8(&[0, 1, 1, 0, 1, 1, 1]), - vec!(0b10110111) - ); - assert_eq!( - reversed_path_to_vec_u8(&[0, 0, 1, 1, 0, 1, 1, 1]), - vec!(0b1, 0b00110111) - ); - assert_eq!( - reversed_path_to_vec_u8(&[1, 0, 0, 1, 1, 0, 1, 1, 1]), - vec!(0b11, 0b00110111) - ); -} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_path_to_vec_u8() { + assert_eq!(reversed_path_to_vec_u8(&[]), vec!(0b1)); + assert_eq!(reversed_path_to_vec_u8(&[0]), vec!(0b10)); + assert_eq!(reversed_path_to_vec_u8(&[1]), vec!(0b11)); + assert_eq!(reversed_path_to_vec_u8(&[0, 0]), vec!(0b100)); + assert_eq!(reversed_path_to_vec_u8(&[0, 1]), vec!(0b101)); + assert_eq!(reversed_path_to_vec_u8(&[1, 0]), vec!(0b110)); + assert_eq!(reversed_path_to_vec_u8(&[1, 1]), vec!(0b111)); + assert_eq!(reversed_path_to_vec_u8(&[1, 1, 1]), vec!(0b1111)); + assert_eq!(reversed_path_to_vec_u8(&[0, 1, 1, 1]), vec!(0b10111)); + assert_eq!(reversed_path_to_vec_u8(&[1, 0, 1, 1, 1]), vec!(0b110111)); + assert_eq!( + reversed_path_to_vec_u8(&[1, 1, 0, 1, 1, 1]), + vec!(0b1110111) + ); + assert_eq!( + reversed_path_to_vec_u8(&[0, 1, 1, 0, 1, 1, 1]), + vec!(0b10110111) + ); + assert_eq!( + reversed_path_to_vec_u8(&[0, 0, 1, 1, 0, 1, 1, 1]), + vec!(0b1, 0b00110111) + ); + assert_eq!( + reversed_path_to_vec_u8(&[1, 0, 0, 1, 1, 0, 1, 1, 1]), + vec!(0b11, 0b00110111) + ); + } -#[test] -fn test_read_cache_lookup() { - let large_max = 30; - let mut rcl = ReadCacheLookup::new(); - - // the only thing cached right now is a nil, right at the top of the tree (ie. `1`) - let hash_of_nil = hash_blob(&[1]); - assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[1]]); - - assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); - - // the atom `1` is not in the tree anywhere - let hash_of_1_atom = hash_blobs(&[&[1], &[1]]); - assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); - - // now let's push a `5` atom to the top - // tree: `(5 . 0)` - let hash_of_5_atom = hash_blobs(&[&[1], &[5]]); - rcl.push(hash_of_5_atom); - let hash_of_cons_5_nil = hash_blobs(&[&[2], &hash_of_5_atom, &hash_of_nil]); - assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[1]]); - assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[2]]); - assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[3]]); - - assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); - - // the atom `1` is still not in the tree anywhere - assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); - - // now let's push a `9` atom to the top - // tree: `(9 . (5 . 0))` - let hash_of_9_atom = hash_blobs(&[&[1], &[9]]); - rcl.push(hash_of_9_atom); - let hash_of_cons_9_cons_5_nil = hash_blobs(&[&[2], &hash_of_9_atom, &hash_of_cons_5_nil]); - - assert_eq!(rcl.find_paths(&hash_of_cons_9_cons_5_nil, large_max), [[1]]); - assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[2]]); - assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[3]]); - assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[5]]); - assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[7]]); - - assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); - - // the atom `1` is still not in the tree anywhere - assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); - - // now let's push a `10` atom to the top - // tree: `(10 . (9 . (5 . 0)))` - - let hash_of_10_atom = hash_blobs(&[&[1], &[10]]); - rcl.push(hash_of_10_atom); - let hash_of_cons_10_cons_9_cons_5_nil = - hash_blobs(&[&[2], &hash_of_10_atom, &hash_of_cons_9_cons_5_nil]); - assert_eq!( - rcl.find_paths(&hash_of_cons_10_cons_9_cons_5_nil, large_max), - [[1]] - ); - assert_eq!(rcl.find_paths(&hash_of_10_atom, large_max), [[2]]); - assert_eq!(rcl.find_paths(&hash_of_cons_9_cons_5_nil, large_max), [[3]]); - assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[5]]); - assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[7]]); - assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[11]]); - assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[15]]); - - assert_eq!( - rcl.count.get(&hash_of_cons_10_cons_9_cons_5_nil).unwrap(), - &1 - ); - assert_eq!(rcl.count.get(&hash_of_10_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); - - // the atom `1` is still not in the tree anywhere - assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); - - // now let's do a `pop2_and_cons` - // tree: `((9 . 10) . (5 . 0))` - // 2 => (9 . 10) - // 3 => (5 . 0) - // 4 => 9 - // 5 => 5 - // 6 => 10 - // 7 => 0 - rcl.pop2_and_cons(); - let hash_of_cons_9_10 = hash_blobs(&[&[2], &hash_of_9_atom, &hash_of_10_atom]); - let hash_of_cons_cons_9_10_cons_5_nil = - hash_blobs(&[&[2], &hash_of_cons_9_10, &hash_of_cons_5_nil]); - assert_eq!( - rcl.find_paths(&hash_of_cons_cons_9_10_cons_5_nil, large_max), - [[1]] - ); - assert_eq!(rcl.find_paths(&hash_of_cons_9_10, large_max), [[2]]); - assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[3]]); - assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[4]]); - assert_eq!(rcl.find_paths(&hash_of_10_atom, large_max), [[6]]); - assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[5]]); - assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[7]]); - - // `(9 . (5 . 0))` is no longer in the tree - assert!(rcl - .find_paths(&hash_of_cons_9_cons_5_nil, large_max) - .is_empty()); - - assert_eq!( - rcl.count.get(&hash_of_cons_cons_9_10_cons_5_nil).unwrap(), - &1 - ); - assert_eq!(rcl.count.get(&hash_of_cons_9_10).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_10_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); - assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); - - // `(9 . (5 . 0))` is no longer in the tree - assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &0); - - // the atom `1` is still not in the tree anywhere - assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); - - assert!(!rcl.count.contains_key(&hash_of_1_atom)); + #[test] + fn test_read_cache_lookup() { + let large_max = 30; + let mut rcl = ReadCacheLookup::new(); + + // the only thing cached right now is a nil, right at the top of the tree (ie. `1`) + let hash_of_nil = hash_blob(&[1]); + assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[1]]); + + assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); + + // the atom `1` is not in the tree anywhere + let hash_of_1_atom = hash_blobs(&[&[1], &[1]]); + assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); + + // now let's push a `5` atom to the top + // tree: `(5 . 0)` + let hash_of_5_atom = hash_blobs(&[&[1], &[5]]); + rcl.push(hash_of_5_atom); + let hash_of_cons_5_nil = hash_blobs(&[&[2], &hash_of_5_atom, &hash_of_nil]); + assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[1]]); + assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[2]]); + assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[3]]); + + assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); + + // the atom `1` is still not in the tree anywhere + assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); + + // now let's push a `9` atom to the top + // tree: `(9 . (5 . 0))` + let hash_of_9_atom = hash_blobs(&[&[1], &[9]]); + rcl.push(hash_of_9_atom); + let hash_of_cons_9_cons_5_nil = hash_blobs(&[&[2], &hash_of_9_atom, &hash_of_cons_5_nil]); + + assert_eq!(rcl.find_paths(&hash_of_cons_9_cons_5_nil, large_max), [[1]]); + assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[2]]); + assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[3]]); + assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[5]]); + assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[7]]); + + assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); + + // the atom `1` is still not in the tree anywhere + assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); + + // now let's push a `10` atom to the top + // tree: `(10 . (9 . (5 . 0)))` + + let hash_of_10_atom = hash_blobs(&[&[1], &[10]]); + rcl.push(hash_of_10_atom); + let hash_of_cons_10_cons_9_cons_5_nil = + hash_blobs(&[&[2], &hash_of_10_atom, &hash_of_cons_9_cons_5_nil]); + assert_eq!( + rcl.find_paths(&hash_of_cons_10_cons_9_cons_5_nil, large_max), + [[1]] + ); + assert_eq!(rcl.find_paths(&hash_of_10_atom, large_max), [[2]]); + assert_eq!(rcl.find_paths(&hash_of_cons_9_cons_5_nil, large_max), [[3]]); + assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[5]]); + assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[7]]); + assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[11]]); + assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[15]]); + + assert_eq!( + rcl.count.get(&hash_of_cons_10_cons_9_cons_5_nil).unwrap(), + &1 + ); + assert_eq!(rcl.count.get(&hash_of_10_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); + + // the atom `1` is still not in the tree anywhere + assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); + + // now let's do a `pop2_and_cons` + // tree: `((9 . 10) . (5 . 0))` + // 2 => (9 . 10) + // 3 => (5 . 0) + // 4 => 9 + // 5 => 5 + // 6 => 10 + // 7 => 0 + rcl.pop2_and_cons(); + let hash_of_cons_9_10 = hash_blobs(&[&[2], &hash_of_9_atom, &hash_of_10_atom]); + let hash_of_cons_cons_9_10_cons_5_nil = + hash_blobs(&[&[2], &hash_of_cons_9_10, &hash_of_cons_5_nil]); + assert_eq!( + rcl.find_paths(&hash_of_cons_cons_9_10_cons_5_nil, large_max), + [[1]] + ); + assert_eq!(rcl.find_paths(&hash_of_cons_9_10, large_max), [[2]]); + assert_eq!(rcl.find_paths(&hash_of_cons_5_nil, large_max), [[3]]); + assert_eq!(rcl.find_paths(&hash_of_9_atom, large_max), [[4]]); + assert_eq!(rcl.find_paths(&hash_of_10_atom, large_max), [[6]]); + assert_eq!(rcl.find_paths(&hash_of_5_atom, large_max), [[5]]); + assert_eq!(rcl.find_paths(&hash_of_nil, large_max), [[7]]); + + // `(9 . (5 . 0))` is no longer in the tree + assert!(rcl + .find_paths(&hash_of_cons_9_cons_5_nil, large_max) + .is_empty()); + + assert_eq!( + rcl.count.get(&hash_of_cons_cons_9_10_cons_5_nil).unwrap(), + &1 + ); + assert_eq!(rcl.count.get(&hash_of_cons_9_10).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_cons_5_nil).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_9_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_10_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_5_atom).unwrap(), &1); + assert_eq!(rcl.count.get(&hash_of_nil).unwrap(), &1); + + // `(9 . (5 . 0))` is no longer in the tree + assert_eq!(rcl.count.get(&hash_of_cons_9_cons_5_nil).unwrap(), &0); + + // the atom `1` is still not in the tree anywhere + assert!(rcl.find_paths(&hash_of_1_atom, large_max).is_empty()); + + assert!(!rcl.count.contains_key(&hash_of_1_atom)); + } } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 314bcaf3..478f8cbf 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -70,36 +70,41 @@ pub fn node_to_bytes(a: &Allocator, node: NodePtr) -> io::Result> { node_to_bytes_limit(a, node, 2000000) } -#[test] -fn test_serialize_limit() { - let mut a = Allocator::new(); +#[cfg(test)] +mod tests { + use super::*; - let leaf = a.new_atom(&[1, 2, 3, 4, 5]).unwrap(); - let l1 = a.new_pair(leaf, leaf).unwrap(); - let l2 = a.new_pair(l1, l1).unwrap(); - let l3 = a.new_pair(l2, l2).unwrap(); + #[test] + fn test_serialize_limit() { + let mut a = Allocator::new(); - { - let buffer = Cursor::new(Vec::new()); - let mut writer = LimitedWriter::new(buffer, 55); - node_to_stream(&a, l3, &mut writer).unwrap(); - let vec = writer.into_inner().into_inner(); - assert_eq!( - vec, - &[ - 0xff, 0xff, 0xff, 133, 1, 2, 3, 4, 5, 133, 1, 2, 3, 4, 5, 0xff, 133, 1, 2, 3, 4, 5, - 133, 1, 2, 3, 4, 5, 0xff, 0xff, 133, 1, 2, 3, 4, 5, 133, 1, 2, 3, 4, 5, 0xff, 133, - 1, 2, 3, 4, 5, 133, 1, 2, 3, 4, 5 - ] - ); - } + let leaf = a.new_atom(&[1, 2, 3, 4, 5]).unwrap(); + let l1 = a.new_pair(leaf, leaf).unwrap(); + let l2 = a.new_pair(l1, l1).unwrap(); + let l3 = a.new_pair(l2, l2).unwrap(); + + { + let buffer = Cursor::new(Vec::new()); + let mut writer = LimitedWriter::new(buffer, 55); + node_to_stream(&a, l3, &mut writer).unwrap(); + let vec = writer.into_inner().into_inner(); + assert_eq!( + vec, + &[ + 0xff, 0xff, 0xff, 133, 1, 2, 3, 4, 5, 133, 1, 2, 3, 4, 5, 0xff, 133, 1, 2, 3, + 4, 5, 133, 1, 2, 3, 4, 5, 0xff, 0xff, 133, 1, 2, 3, 4, 5, 133, 1, 2, 3, 4, 5, + 0xff, 133, 1, 2, 3, 4, 5, 133, 1, 2, 3, 4, 5 + ] + ); + } - { - let buffer = Cursor::new(Vec::new()); - let mut writer = LimitedWriter::new(buffer, 54); - assert_eq!( - node_to_stream(&a, l3, &mut writer).unwrap_err().kind(), - io::ErrorKind::OutOfMemory - ); + { + let buffer = Cursor::new(Vec::new()); + let mut writer = LimitedWriter::new(buffer, 54); + assert_eq!( + node_to_stream(&a, l3, &mut writer).unwrap_err().kind(), + io::ErrorKind::OutOfMemory + ); + } } } diff --git a/src/serde/ser_br.rs b/src/serde/ser_br.rs index c7dc0d27..9bc63e04 100644 --- a/src/serde/ser_br.rs +++ b/src/serde/ser_br.rs @@ -18,13 +18,6 @@ enum ReadOp { Cons, } -// these test cases were produced by: - -// from chia.types.blockchain_format.program import Program -// a = Program.to(...) -// print(bytes(a).hex()) -// print(a.get_tree_hash().hex()) - pub fn node_to_stream_backrefs( allocator: &Allocator, node: NodePtr, @@ -97,26 +90,22 @@ pub fn node_to_bytes_backrefs(a: &Allocator, node: NodePtr) -> io::Result io::Result { } } -#[test] -fn test_tree_hash_max_single_byte() { - let mut ctx = Sha256::new(); - ctx.update([1_u8]); - ctx.update([0x7f_u8]); - let mut cursor = Cursor::<&[u8]>::new(&[0x7f_u8]); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap(), - ctx.finalize().as_slice() - ); -} - -#[test] -fn test_tree_hash_one() { - let mut ctx = Sha256::new(); - ctx.update([1_u8]); - ctx.update([1_u8]); - let mut cursor = Cursor::<&[u8]>::new(&[1_u8]); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap(), - ctx.finalize().as_slice() - ); -} - -#[test] -fn test_tree_hash_zero() { - let mut ctx = Sha256::new(); - ctx.update([1_u8]); - ctx.update([0_u8]); - let mut cursor = Cursor::<&[u8]>::new(&[0_u8]); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap(), - ctx.finalize().as_slice() - ); -} - -#[test] -fn test_tree_hash_nil() { - let mut ctx = Sha256::new(); - ctx.update([1_u8]); - let mut cursor = Cursor::<&[u8]>::new(&[0x80_u8]); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap(), - ctx.finalize().as_slice() - ); -} - -#[test] -fn test_tree_hash_overlong() { - let mut cursor = Cursor::<&[u8]>::new(&[0x8f, 0xff]); - let e = tree_hash_from_stream(&mut cursor).unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - - let mut cursor = Cursor::<&[u8]>::new(&[0b11001111, 0xff]); - let e = tree_hash_from_stream(&mut cursor).unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - - let mut cursor = Cursor::<&[u8]>::new(&[0b11001111, 0xff, 0, 0]); - let e = tree_hash_from_stream(&mut cursor).unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); -} - #[cfg(test)] -use hex::FromHex; - -// these test cases were produced by: - -// from chia.types.blockchain_format.program import Program -// a = Program.to(...) -// print(bytes(a).hex()) -// print(a.get_tree_hash().hex()) - -#[test] -fn test_tree_hash_list() { - // this is the list (1 (2 (3 (4 (5 ()))))) - let buf = Vec::from_hex("ff01ff02ff03ff04ff0580").unwrap(); - let mut cursor = Cursor::<&[u8]>::new(&buf); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap().to_vec(), - Vec::from_hex("123190dddde51acfc61f48429a879a7b905d1726a52991f7d63349863d06b1b6").unwrap() - ); -} - -#[test] -fn test_tree_hash_tree() { - // this is the tree ((1, 2), (3, 4)) - let buf = Vec::from_hex("ffff0102ff0304").unwrap(); - let mut cursor = Cursor::<&[u8]>::new(&buf); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap().to_vec(), - Vec::from_hex("2824018d148bc6aed0847e2c86aaa8a5407b916169f15b12cea31fa932fc4c8d").unwrap() - ); -} - -#[test] -fn test_tree_hash_tree_large_atom() { - // this is the tree ((1, 2), (3, b"foobar")) - let buf = Vec::from_hex("ffff0102ff0386666f6f626172").unwrap(); - let mut cursor = Cursor::<&[u8]>::new(&buf); - assert_eq!( - tree_hash_from_stream(&mut cursor).unwrap().to_vec(), - Vec::from_hex("b28d5b401bd02b65b7ed93de8e916cfc488738323e568bcca7e032c3a97a12e4").unwrap() - ); -} - -#[cfg(test)] -mod test { +mod tests { use super::*; - use crate::serde::node_from_bytes_backrefs; - use crate::Allocator; - use rstest::rstest; + + use hex::FromHex; #[test] - fn test_serialized_length_from_bytes_trusted() { - assert_eq!( - serialized_length_from_bytes_trusted(&[0x7f, 0x00, 0x00, 0x00]).unwrap(), - 1 - ); + fn test_tree_hash_max_single_byte() { + let mut ctx = Sha256::new(); + ctx.update([1_u8]); + ctx.update([0x7f_u8]); + let mut cursor = Cursor::<&[u8]>::new(&[0x7f_u8]); assert_eq!( - serialized_length_from_bytes_trusted(&[0x80, 0x00, 0x00, 0x00]).unwrap(), - 1 + tree_hash_from_stream(&mut cursor).unwrap(), + ctx.finalize().as_slice() ); + } + + #[test] + fn test_tree_hash_one() { + let mut ctx = Sha256::new(); + ctx.update([1_u8]); + ctx.update([1_u8]); + let mut cursor = Cursor::<&[u8]>::new(&[1_u8]); assert_eq!( - serialized_length_from_bytes_trusted(&[0xff, 0x00, 0x00, 0x00]).unwrap(), - 3 + tree_hash_from_stream(&mut cursor).unwrap(), + ctx.finalize().as_slice() ); + } + + #[test] + fn test_tree_hash_zero() { + let mut ctx = Sha256::new(); + ctx.update([1_u8]); + ctx.update([0_u8]); + let mut cursor = Cursor::<&[u8]>::new(&[0_u8]); assert_eq!( - serialized_length_from_bytes_trusted(&[0xff, 0x01, 0xff, 0x80, 0x80, 0x00]).unwrap(), - 5 + tree_hash_from_stream(&mut cursor).unwrap(), + ctx.finalize().as_slice() ); + } - // this is an invalid back-ref - // but it's not validated + #[test] + fn test_tree_hash_nil() { + let mut ctx = Sha256::new(); + ctx.update([1_u8]); + let mut cursor = Cursor::<&[u8]>::new(&[0x80_u8]); assert_eq!( - serialized_length_from_bytes_trusted(&[0xff, 0x01, 0xff, 0xfe, 0x10, 0x80, 0x00]) - .unwrap(), - 6 + tree_hash_from_stream(&mut cursor).unwrap(), + ctx.finalize().as_slice() ); + } - let e = serialized_length_from_bytes_trusted(&[0x8f, 0xff]).unwrap_err(); + #[test] + fn test_tree_hash_overlong() { + let mut cursor = Cursor::<&[u8]>::new(&[0x8f, 0xff]); + let e = tree_hash_from_stream(&mut cursor).unwrap_err(); assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - let e = serialized_length_from_bytes_trusted(&[0b11001111, 0xff]).unwrap_err(); + let mut cursor = Cursor::<&[u8]>::new(&[0b11001111, 0xff]); + let e = tree_hash_from_stream(&mut cursor).unwrap_err(); assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - let e = serialized_length_from_bytes_trusted(&[0b11001111, 0xff, 0, 0]).unwrap_err(); + let mut cursor = Cursor::<&[u8]>::new(&[0b11001111, 0xff, 0, 0]); + let e = tree_hash_from_stream(&mut cursor).unwrap_err(); assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); + } + // these test cases were produced by: + + // from chia.types.blockchain_format.program import Program + // a = Program.to(...) + // print(bytes(a).hex()) + // print(a.get_tree_hash().hex()) + + #[test] + fn test_tree_hash_list() { + // this is the list (1 (2 (3 (4 (5 ()))))) + let buf = Vec::from_hex("ff01ff02ff03ff04ff0580").unwrap(); + let mut cursor = Cursor::<&[u8]>::new(&buf); assert_eq!( - serialized_length_from_bytes_trusted(&[ - 0x8f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ]) - .unwrap(), - 16 + tree_hash_from_stream(&mut cursor).unwrap().to_vec(), + Vec::from_hex("123190dddde51acfc61f48429a879a7b905d1726a52991f7d63349863d06b1b6") + .unwrap() ); } #[test] - fn test_serialized_length_from_bytes() { - use std::io::ErrorKind; - assert_eq!( - serialized_length_from_bytes(&[0x7f, 0x00, 0x00, 0x00]).unwrap(), - 1 - ); - assert_eq!( - serialized_length_from_bytes(&[0x80, 0x00, 0x00, 0x00]).unwrap(), - 1 - ); - assert_eq!( - serialized_length_from_bytes(&[0xff, 0x00, 0x00, 0x00]).unwrap(), - 3 - ); + fn test_tree_hash_tree() { + // this is the tree ((1, 2), (3, 4)) + let buf = Vec::from_hex("ffff0102ff0304").unwrap(); + let mut cursor = Cursor::<&[u8]>::new(&buf); assert_eq!( - serialized_length_from_bytes(&[0xff, 0x01, 0xff, 0x80, 0x80, 0x00]).unwrap(), - 5 + tree_hash_from_stream(&mut cursor).unwrap().to_vec(), + Vec::from_hex("2824018d148bc6aed0847e2c86aaa8a5407b916169f15b12cea31fa932fc4c8d") + .unwrap() ); + } - // this is an invalid back-ref - let e = - serialized_length_from_bytes(&[0xff, 0x01, 0xff, 0xfe, 0x10, 0x80, 0x00]).unwrap_err(); - assert_eq!(e.kind(), ErrorKind::Other); - assert_eq!(e.to_string(), "path into atom"); - - let e = serialized_length_from_bytes(&[0x8f, 0xff]).unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - - let e = serialized_length_from_bytes(&[0b11001111, 0xff]).unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - - let e = serialized_length_from_bytes(&[0b11001111, 0xff, 0, 0]).unwrap_err(); - assert_eq!(e.kind(), bad_encoding().kind()); - assert_eq!(e.to_string(), "bad encoding"); - + #[test] + fn test_tree_hash_tree_large_atom() { + // this is the tree ((1, 2), (3, b"foobar")) + let buf = Vec::from_hex("ffff0102ff0386666f6f626172").unwrap(); + let mut cursor = Cursor::<&[u8]>::new(&buf); assert_eq!( - serialized_length_from_bytes(&[0x8f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - .unwrap(), - 16 + tree_hash_from_stream(&mut cursor).unwrap().to_vec(), + Vec::from_hex("b28d5b401bd02b65b7ed93de8e916cfc488738323e568bcca7e032c3a97a12e4") + .unwrap() ); } - #[rstest] - // ("foobar" "foobar") - #[case("ff86666f6f626172ff86666f6f62617280")] - // ("foobar" "foobar") - #[case("ff86666f6f626172fe01")] - // ((1 2 3 4) 1 2 3 4) - #[case("ffff01ff02ff03ff0480ff01ff02ff03ff0480")] - // ((1 2 3 4) 1 2 3 4) - #[case("ffff01ff02ff03ff0480fe02")] - // `(((((a_very_long_repeated_string . 1) . (2 . 3)) . ((4 . 5) . (6 . 7))) . (8 . 9)) 10 a_very_long_repeated_string)` - #[case( - "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\ + #[cfg(test)] + mod test { + use super::*; + use crate::serde::node_from_bytes_backrefs; + use crate::Allocator; + use rstest::rstest; + + #[test] + fn test_serialized_length_from_bytes_trusted() { + assert_eq!( + serialized_length_from_bytes_trusted(&[0x7f, 0x00, 0x00, 0x00]).unwrap(), + 1 + ); + assert_eq!( + serialized_length_from_bytes_trusted(&[0x80, 0x00, 0x00, 0x00]).unwrap(), + 1 + ); + assert_eq!( + serialized_length_from_bytes_trusted(&[0xff, 0x00, 0x00, 0x00]).unwrap(), + 3 + ); + assert_eq!( + serialized_length_from_bytes_trusted(&[0xff, 0x01, 0xff, 0x80, 0x80, 0x00]) + .unwrap(), + 5 + ); + + // this is an invalid back-ref + // but it's not validated + assert_eq!( + serialized_length_from_bytes_trusted(&[0xff, 0x01, 0xff, 0xfe, 0x10, 0x80, 0x00]) + .unwrap(), + 6 + ); + + let e = serialized_length_from_bytes_trusted(&[0x8f, 0xff]).unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + let e = serialized_length_from_bytes_trusted(&[0b11001111, 0xff]).unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + let e = serialized_length_from_bytes_trusted(&[0b11001111, 0xff, 0, 0]).unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + assert_eq!( + serialized_length_from_bytes_trusted(&[ + 0x8f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]) + .unwrap(), + 16 + ); + } + + #[test] + fn test_serialized_length_from_bytes() { + use std::io::ErrorKind; + assert_eq!( + serialized_length_from_bytes(&[0x7f, 0x00, 0x00, 0x00]).unwrap(), + 1 + ); + assert_eq!( + serialized_length_from_bytes(&[0x80, 0x00, 0x00, 0x00]).unwrap(), + 1 + ); + assert_eq!( + serialized_length_from_bytes(&[0xff, 0x00, 0x00, 0x00]).unwrap(), + 3 + ); + assert_eq!( + serialized_length_from_bytes(&[0xff, 0x01, 0xff, 0x80, 0x80, 0x00]).unwrap(), + 5 + ); + + // this is an invalid back-ref + let e = serialized_length_from_bytes(&[0xff, 0x01, 0xff, 0xfe, 0x10, 0x80, 0x00]) + .unwrap_err(); + assert_eq!(e.kind(), ErrorKind::Other); + assert_eq!(e.to_string(), "path into atom"); + + let e = serialized_length_from_bytes(&[0x8f, 0xff]).unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + let e = serialized_length_from_bytes(&[0b11001111, 0xff]).unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + let e = serialized_length_from_bytes(&[0b11001111, 0xff, 0, 0]).unwrap_err(); + assert_eq!(e.kind(), bad_encoding().kind()); + assert_eq!(e.to_string(), "bad encoding"); + + assert_eq!( + serialized_length_from_bytes(&[0x8f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + .unwrap(), + 16 + ); + } + + #[rstest] + // ("foobar" "foobar") + #[case("ff86666f6f626172ff86666f6f62617280")] + // ("foobar" "foobar") + #[case("ff86666f6f626172fe01")] + // ((1 2 3 4) 1 2 3 4) + #[case("ffff01ff02ff03ff0480ff01ff02ff03ff0480")] + // ((1 2 3 4) 1 2 3 4) + #[case("ffff01ff02ff03ff0480fe02")] + // `(((((a_very_long_repeated_string . 1) . (2 . 3)) . ((4 . 5) . (6 . 7))) . (8 . 9)) 10 a_very_long_repeated_string)` + #[case( + "ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\ 05ff0607ff0809ff0aff9b615f766572795f6c6f6e675f72657065617465645f737472696e6780" - )] - #[case("ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff0405ff0607ff0809ff0afffe4180")] - #[case( - "ff01ffffffa022cf3c17be4e0e0e0b2e2a3f6dd1ee955528f737f0cb724247bc2e4a776cb989ff\ + )] + #[case("ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff0405ff0607ff0809ff0afffe4180")] + #[case( + "ff01ffffffa022cf3c17be4e0e0e0b2e2a3f6dd1ee955528f737f0cb724247bc2e4a776cb989ff\ ff02ffff01ff02ffff01ff02ffff03ffff18ff2fffff010180ffff01ff02ff36ffff04ff02ffff\ 04ff05ffff04ff17ffff04ffff02ff26ffff04ff02ffff04ff0bff80808080ffff04ff2fffff04\ ff0bffff04ff5fff808080808080808080ffff01ff088080fe81ffffff04ffff01ffffffff4602\ @@ -513,15 +520,16 @@ eb09d18d83bd56482aee566820f5afdce595b3ed095eb36c5cef9301a5383a3b9a6c1a94a85e4f\ 982e1fa3af2c99087e5f6df8b887d30c109f71043671683a1ae985d7d874fbe07dfa6d88b70100\ 00001868747470733a2f2f6368696170702e68706f6f6c2e636f6d0000004080ffa0fc0b1e9409\ ae5c3c40c50832a7aecc0b3ba4646568a00c01289c45e1f03b2b488080808080" - )] - fn serialized_length_with_backrefs(#[case] serialization_as_hex: &str) { - let buf = Vec::from_hex(serialization_as_hex).unwrap(); - let len = serialized_length_from_bytes(&buf).expect("serialized_length_from_bytes"); + )] + fn serialized_length_with_backrefs(#[case] serialization_as_hex: &str) { + let buf = Vec::from_hex(serialization_as_hex).unwrap(); + let len = serialized_length_from_bytes(&buf).expect("serialized_length_from_bytes"); - // make sure the serialization is valid - let mut allocator = Allocator::new(); - assert!(node_from_bytes_backrefs(&mut allocator, &buf).is_ok()); + // make sure the serialization is valid + let mut allocator = Allocator::new(); + assert!(node_from_bytes_backrefs(&mut allocator, &buf).is_ok()); - assert_eq!(len, buf.len() as u64); + assert_eq!(len, buf.len() as u64); + } } } diff --git a/src/serde/write_atom.rs b/src/serde/write_atom.rs index e42f13fa..1ab4f2fb 100644 --- a/src/serde/write_atom.rs +++ b/src/serde/write_atom.rs @@ -52,124 +52,129 @@ pub fn write_atom(f: &mut W, atom: &[u8]) -> io::Result<()> { f.write_all(atom) } -#[test] -fn test_write_atom_encoding_prefix_with_size() { - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0, 0).is_ok()); - assert_eq!(buf, vec![0x80]); +#[cfg(test)] +mod tests { + use super::*; - for v in 0..0x7f { + #[test] + fn test_write_atom_encoding_prefix_with_size() { let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, v, 1).is_ok()); - assert_eq!(buf, vec![]); - } + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0, 0).is_ok()); + assert_eq!(buf, vec![0x80]); + + for v in 0..0x7f { + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, v, 1).is_ok()); + assert_eq!(buf, vec![]); + } + + for v in 0x80..0xff { + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, v, 1).is_ok()); + assert_eq!(buf, vec![0x81]); + } + + for size in 0x1_u8..0x3f_u8 { + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, size as u64).is_ok()); + assert_eq!(buf, vec![0x80 + size]); + } - for v in 0x80..0xff { let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, v, 1).is_ok()); - assert_eq!(buf, vec![0x81]); - } + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0b111111).is_ok()); + assert_eq!(buf, vec![0b10111111]); - for size in 0x1_u8..0x3f_u8 { let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, size as u64).is_ok()); - assert_eq!(buf, vec![0x80 + size]); - } + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0b1000000).is_ok()); + assert_eq!(buf, vec![0b11000000, 0b1000000]); + + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0xfffff).is_ok()); + assert_eq!(buf, vec![0b11101111, 0xff, 0xff]); + + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0xffffff).is_ok()); + assert_eq!(buf, vec![0b11110000, 0xff, 0xff, 0xff]); + + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0xffffffff).is_ok()); + assert_eq!(buf, vec![0b11111000, 0xff, 0xff, 0xff, 0xff]); + + // this is the largest possible atom size + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0x3ffffffff).is_ok()); + assert_eq!(buf, vec![0b11111011, 0xff, 0xff, 0xff, 0xff]); - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0b111111).is_ok()); - assert_eq!(buf, vec![0b10111111]); - - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0b1000000).is_ok()); - assert_eq!(buf, vec![0b11000000, 0b1000000]); - - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0xfffff).is_ok()); - assert_eq!(buf, vec![0b11101111, 0xff, 0xff]); - - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0xffffff).is_ok()); - assert_eq!(buf, vec![0b11110000, 0xff, 0xff, 0xff]); - - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0xffffffff).is_ok()); - assert_eq!(buf, vec![0b11111000, 0xff, 0xff, 0xff, 0xff]); - - // this is the largest possible atom size - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0x3ffffffff).is_ok()); - assert_eq!(buf, vec![0b11111011, 0xff, 0xff, 0xff, 0xff]); - - // this is too large - let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0x400000000).is_err()); - - for (size, expected_prefix) in [ - (0x1, vec![0x81]), - (0x2, vec![0x82]), - (0x3f, vec![0xbf]), - (0x40, vec![0xc0, 0x40]), - (0x1fff, vec![0xdf, 0xff]), - (0x2000, vec![0xe0, 0x20, 0x00]), - (0xf_ffff, vec![0xef, 0xff, 0xff]), - (0x10_0000, vec![0xf0, 0x10, 0x00, 0x00]), - (0x7ff_ffff, vec![0xf7, 0xff, 0xff, 0xff]), - (0x800_0000, vec![0xf8, 0x08, 0x00, 0x00, 0x00]), - (0x3_ffff_ffff, vec![0xfb, 0xff, 0xff, 0xff, 0xff]), - ] { + // this is too large let mut buf = Vec::::new(); - assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, size).is_ok()); - assert_eq!(buf, expected_prefix); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, 0x400000000).is_err()); + + for (size, expected_prefix) in [ + (0x1, vec![0x81]), + (0x2, vec![0x82]), + (0x3f, vec![0xbf]), + (0x40, vec![0xc0, 0x40]), + (0x1fff, vec![0xdf, 0xff]), + (0x2000, vec![0xe0, 0x20, 0x00]), + (0xf_ffff, vec![0xef, 0xff, 0xff]), + (0x10_0000, vec![0xf0, 0x10, 0x00, 0x00]), + (0x7ff_ffff, vec![0xf7, 0xff, 0xff, 0xff]), + (0x800_0000, vec![0xf8, 0x08, 0x00, 0x00, 0x00]), + (0x3_ffff_ffff, vec![0xfb, 0xff, 0xff, 0xff, 0xff]), + ] { + let mut buf = Vec::::new(); + assert!(write_atom_encoding_prefix_with_size(&mut buf, 0xaa, size).is_ok()); + assert_eq!(buf, expected_prefix); + } } -} -#[test] -fn test_write_atom() { - let mut buf = Vec::::new(); - assert!(write_atom(&mut buf, &[]).is_ok()); - assert_eq!(buf, vec![0b10000000]); - - let mut buf = Vec::::new(); - assert!(write_atom(&mut buf, &[0x00]).is_ok()); - assert_eq!(buf, vec![0b00000000]); - - let mut buf = Vec::::new(); - assert!(write_atom(&mut buf, &[0x7f]).is_ok()); - assert_eq!(buf, vec![0x7f]); - - let mut buf = Vec::::new(); - assert!(write_atom(&mut buf, &[0x80]).is_ok()); - assert_eq!(buf, vec![0x81, 0x80]); - - let mut buf = Vec::::new(); - assert!(write_atom(&mut buf, &[0xff]).is_ok()); - assert_eq!(buf, vec![0x81, 0xff]); - - let mut buf = Vec::::new(); - assert!(write_atom(&mut buf, &[0xaa, 0xbb]).is_ok()); - assert_eq!(buf, vec![0x82, 0xaa, 0xbb]); - - for (size, mut expected_prefix) in [ - (0x1, vec![0x81]), - (0x2, vec![0x82]), - (0x3f, vec![0xbf]), - (0x40, vec![0xc0, 0x40]), - (0x1fff, vec![0xdf, 0xff]), - (0x2000, vec![0xe0, 0x20, 0x00]), - (0xf_ffff, vec![0xef, 0xff, 0xff]), - (0x10_0000, vec![0xf0, 0x10, 0x00, 0x00]), - (0x7ff_ffff, vec![0xf7, 0xff, 0xff, 0xff]), - (0x800_0000, vec![0xf8, 0x08, 0x00, 0x00, 0x00]), - // the next one represents 17 GB of memory, which it then has to serialize - // so let's not do it until some time in the future when all machines have - // 64 GB of memory - // (0x3_ffff_ffff, vec![0xfb, 0xff, 0xff, 0xff, 0xff]), - ] { + #[test] + fn test_write_atom() { + let mut buf = Vec::::new(); + assert!(write_atom(&mut buf, &[]).is_ok()); + assert_eq!(buf, vec![0b10000000]); + + let mut buf = Vec::::new(); + assert!(write_atom(&mut buf, &[0x00]).is_ok()); + assert_eq!(buf, vec![0b00000000]); + + let mut buf = Vec::::new(); + assert!(write_atom(&mut buf, &[0x7f]).is_ok()); + assert_eq!(buf, vec![0x7f]); + + let mut buf = Vec::::new(); + assert!(write_atom(&mut buf, &[0x80]).is_ok()); + assert_eq!(buf, vec![0x81, 0x80]); + + let mut buf = Vec::::new(); + assert!(write_atom(&mut buf, &[0xff]).is_ok()); + assert_eq!(buf, vec![0x81, 0xff]); + let mut buf = Vec::::new(); - let atom = vec![0xaa; size]; - assert!(write_atom(&mut buf, &atom).is_ok()); - expected_prefix.extend(atom); - assert_eq!(buf, expected_prefix); + assert!(write_atom(&mut buf, &[0xaa, 0xbb]).is_ok()); + assert_eq!(buf, vec![0x82, 0xaa, 0xbb]); + + for (size, mut expected_prefix) in [ + (0x1, vec![0x81]), + (0x2, vec![0x82]), + (0x3f, vec![0xbf]), + (0x40, vec![0xc0, 0x40]), + (0x1fff, vec![0xdf, 0xff]), + (0x2000, vec![0xe0, 0x20, 0x00]), + (0xf_ffff, vec![0xef, 0xff, 0xff]), + (0x10_0000, vec![0xf0, 0x10, 0x00, 0x00]), + (0x7ff_ffff, vec![0xf7, 0xff, 0xff, 0xff]), + (0x800_0000, vec![0xf8, 0x08, 0x00, 0x00, 0x00]), + // the next one represents 17 GB of memory, which it then has to serialize + // so let's not do it until some time in the future when all machines have + // 64 GB of memory + // (0x3_ffff_ffff, vec![0xfb, 0xff, 0xff, 0xff, 0xff]), + ] { + let mut buf = Vec::::new(); + let atom = vec![0xaa; size]; + assert!(write_atom(&mut buf, &atom).is_ok()); + expected_prefix.extend(atom); + assert_eq!(buf, expected_prefix); + } } } diff --git a/src/test_ops.rs b/src/test_ops.rs index a9b76d4f..6957eafc 100644 --- a/src/test_ops.rs +++ b/src/test_ops.rs @@ -185,313 +185,320 @@ pub fn node_eq(allocator: &Allocator, s1: NodePtr, s2: NodePtr) -> bool { } } -type Opf = fn(&mut Allocator, NodePtr, Cost) -> Response; - -// the input is a list of test cases, each item is a tuple of: -// (function pointer to test, list of arguments, optional result) -// if the result is None, the call is expected to fail -fn run_op_test(op: &Opf, args_str: &str, expected: &str, expected_cost: u64) { - let mut a = Allocator::new(); - - let (args, rest) = parse_list(&mut a, args_str); - assert_eq!(rest, ""); - let result = op(&mut a, args, 10000000000 as Cost); - match result { - Err(e) => { - println!("Error: {}", e.1); - assert_eq!(expected, "FAIL"); - } - Ok(Reduction(cost, ret_value)) => { - assert_eq!(cost, expected_cost); - let (expected, rest) = parse_exp(&mut a, expected); - assert_eq!(rest, ""); - assert!(node_eq(&a, ret_value, expected)); - } - } -} - #[cfg(test)] -use rstest::rstest; +mod tests { + use super::*; -#[cfg(test)] -#[rstest] -#[case("test-core-ops")] -#[case("test-more-ops")] -#[case("test-bls-ops")] -#[case("test-blspy-g1")] -#[case("test-blspy-g2")] -#[case("test-blspy-hash")] -#[case("test-blspy-pairing")] -#[case("test-blspy-verify")] -#[case("test-bls-zk")] -#[case("test-secp-verify")] -#[case("test-secp256k1")] -#[case("test-secp256r1")] -#[case("test-modpow")] -#[case("test-sha256")] -fn test_ops(#[case] filename: &str) { - use std::fs::read_to_string; - - let filename = format!("op-tests/{filename}.txt"); - - let funs = HashMap::from([ - ("i", op_if as Opf), - ("c", op_cons as Opf), - ("f", op_first as Opf), - ("r", op_rest as Opf), - ("l", op_listp as Opf), - ("x", op_raise as Opf), - ("=", op_eq as Opf), - ("sha256", op_sha256 as Opf), - ("+", op_add as Opf), - ("-", op_subtract as Opf), - ("*", op_multiply as Opf), - ("/", op_div as Opf), - ("divmod", op_divmod as Opf), - ("%", op_mod as Opf), - ("substr", op_substr as Opf), - ("strlen", op_strlen as Opf), - ("point_add", op_point_add as Opf), - ("pubkey_for_exp", op_pubkey_for_exp as Opf), - ("concat", op_concat as Opf), - (">", op_gr as Opf), - (">s", op_gr_bytes as Opf), - ("logand", op_logand as Opf), - ("logior", op_logior as Opf), - ("logxor", op_logxor as Opf), - ("lognot", op_lognot as Opf), - ("ash", op_ash as Opf), - ("lsh", op_lsh as Opf), - ("not", op_not as Opf), - ("any", op_any as Opf), - ("all", op_all as Opf), - //the BLS extension - ("coinid", op_coinid as Opf), - ("g1_add", op_point_add as Opf), - ("g1_subtract", op_bls_g1_subtract as Opf), - ("g1_multiply", op_bls_g1_multiply as Opf), - ("g1_negate", op_bls_g1_negate as Opf), - ("g2_add", op_bls_g2_add as Opf), - ("g2_subtract", op_bls_g2_subtract as Opf), - ("g2_multiply", op_bls_g2_multiply as Opf), - ("g2_negate", op_bls_g2_negate as Opf), - ("g1_map", op_bls_map_to_g1 as Opf), - ("g2_map", op_bls_map_to_g2 as Opf), - ("bls_pairing_identity", op_bls_pairing_identity as Opf), - ("bls_verify", op_bls_verify as Opf), - ("secp256k1_verify", op_secp256k1_verify as Opf), - ("secp256r1_verify", op_secp256r1_verify as Opf), - ("modpow", op_modpow as Opf), - ]); - - println!("Test cases from: {filename}"); - let test_cases = read_to_string(filename).expect("test file not found"); - for t in test_cases.split('\n') { - let t = t.trim(); - if t.is_empty() { - continue; + #[cfg(feature = "pre-eval")] + use crate::chia_dialect::{ChiaDialect, NO_UNKNOWN_OPS}; + + #[cfg(feature = "pre-eval")] + use crate::run_program::run_program_with_pre_eval; + + #[cfg(feature = "pre-eval")] + use std::cell::RefCell; + + #[cfg(feature = "pre-eval")] + use std::collections::HashSet; + + // Allows move closures to tear off a reference and move it. + // Allows interior mutability inside Fn traits. + #[cfg(feature = "pre-eval")] + use std::rc::Rc; + + use rstest::rstest; + + type Opf = fn(&mut Allocator, NodePtr, Cost) -> Response; + + // the input is a list of test cases, each item is a tuple of: + // (function pointer to test, list of arguments, optional result) + // if the result is None, the call is expected to fail + fn run_op_test(op: &Opf, args_str: &str, expected: &str, expected_cost: u64) { + let mut a = Allocator::new(); + + let (args, rest) = parse_list(&mut a, args_str); + assert_eq!(rest, ""); + let result = op(&mut a, args, 10000000000 as Cost); + match result { + Err(e) => { + println!("Error: {}", e.1); + assert_eq!(expected, "FAIL"); + } + Ok(Reduction(cost, ret_value)) => { + assert_eq!(cost, expected_cost); + let (expected, rest) = parse_exp(&mut a, expected); + assert_eq!(rest, ""); + assert!(node_eq(&a, ret_value, expected)); + } } - // ignore comments - if t.starts_with(';') { - continue; + } + + #[rstest] + #[case("test-core-ops")] + #[case("test-more-ops")] + #[case("test-bls-ops")] + #[case("test-blspy-g1")] + #[case("test-blspy-g2")] + #[case("test-blspy-hash")] + #[case("test-blspy-pairing")] + #[case("test-blspy-verify")] + #[case("test-bls-zk")] + #[case("test-secp-verify")] + #[case("test-secp256k1")] + #[case("test-secp256r1")] + #[case("test-modpow")] + #[case("test-sha256")] + fn test_ops(#[case] filename: &str) { + use std::fs::read_to_string; + + let filename = format!("op-tests/{filename}.txt"); + + let funs = HashMap::from([ + ("i", op_if as Opf), + ("c", op_cons as Opf), + ("f", op_first as Opf), + ("r", op_rest as Opf), + ("l", op_listp as Opf), + ("x", op_raise as Opf), + ("=", op_eq as Opf), + ("sha256", op_sha256 as Opf), + ("+", op_add as Opf), + ("-", op_subtract as Opf), + ("*", op_multiply as Opf), + ("/", op_div as Opf), + ("divmod", op_divmod as Opf), + ("%", op_mod as Opf), + ("substr", op_substr as Opf), + ("strlen", op_strlen as Opf), + ("point_add", op_point_add as Opf), + ("pubkey_for_exp", op_pubkey_for_exp as Opf), + ("concat", op_concat as Opf), + (">", op_gr as Opf), + (">s", op_gr_bytes as Opf), + ("logand", op_logand as Opf), + ("logior", op_logior as Opf), + ("logxor", op_logxor as Opf), + ("lognot", op_lognot as Opf), + ("ash", op_ash as Opf), + ("lsh", op_lsh as Opf), + ("not", op_not as Opf), + ("any", op_any as Opf), + ("all", op_all as Opf), + //the BLS extension + ("coinid", op_coinid as Opf), + ("g1_add", op_point_add as Opf), + ("g1_subtract", op_bls_g1_subtract as Opf), + ("g1_multiply", op_bls_g1_multiply as Opf), + ("g1_negate", op_bls_g1_negate as Opf), + ("g2_add", op_bls_g2_add as Opf), + ("g2_subtract", op_bls_g2_subtract as Opf), + ("g2_multiply", op_bls_g2_multiply as Opf), + ("g2_negate", op_bls_g2_negate as Opf), + ("g1_map", op_bls_map_to_g1 as Opf), + ("g2_map", op_bls_map_to_g2 as Opf), + ("bls_pairing_identity", op_bls_pairing_identity as Opf), + ("bls_verify", op_bls_verify as Opf), + ("secp256k1_verify", op_secp256k1_verify as Opf), + ("secp256r1_verify", op_secp256r1_verify as Opf), + ("modpow", op_modpow as Opf), + ]); + + println!("Test cases from: {filename}"); + let test_cases = read_to_string(filename).expect("test file not found"); + for t in test_cases.split('\n') { + let t = t.trim(); + if t.is_empty() { + continue; + } + // ignore comments + if t.starts_with(';') { + continue; + } + let (op_name, t) = t.split_once(' ').unwrap(); + let op = funs + .get(op_name) + .unwrap_or_else(|| panic!("couldn't find operator \"{op_name}\"")); + let (args, out) = t.split_once("=>").unwrap(); + let (expected, expected_cost) = if out.contains('|') { + out.split_once('|').unwrap() + } else { + (out, "0") + }; + + println!("({} {}) => {}", op_name, args.trim(), expected.trim()); + run_op_test( + op, + args.trim(), + expected.trim(), + expected_cost.trim().parse().unwrap(), + ); } - let (op_name, t) = t.split_once(' ').unwrap(); - let op = funs - .get(op_name) - .unwrap_or_else(|| panic!("couldn't find operator \"{op_name}\"")); - let (args, out) = t.split_once("=>").unwrap(); - let (expected, expected_cost) = if out.contains('|') { - out.split_once('|').unwrap() - } else { - (out, "0") - }; + } - println!("({} {}) => {}", op_name, args.trim(), expected.trim()); - run_op_test( - op, - args.trim(), - expected.trim(), - expected_cost.trim().parse().unwrap(), - ); + #[test] + fn test_single_argument_raise_atom() { + let mut allocator = Allocator::new(); + let a1 = allocator.new_atom(&[65]).unwrap(); + let args = allocator.new_pair(a1, allocator.nil()).unwrap(); + let result = op_raise(&mut allocator, args, 100000); + assert_eq!(result, Err(EvalErr(a1, "clvm raise".to_string()))); } -} -#[test] -fn test_single_argument_raise_atom() { - let mut allocator = Allocator::new(); - let a1 = allocator.new_atom(&[65]).unwrap(); - let args = allocator.new_pair(a1, allocator.nil()).unwrap(); - let result = op_raise(&mut allocator, args, 100000); - assert_eq!(result, Err(EvalErr(a1, "clvm raise".to_string()))); -} + #[test] + fn test_single_argument_raise_pair() { + let mut allocator = Allocator::new(); + let a1 = allocator.new_atom(&[65]).unwrap(); + let a2 = allocator.new_atom(&[66]).unwrap(); + // (a2) + let mut args = allocator.new_pair(a2, allocator.nil()).unwrap(); + // (a1 a2) + args = allocator.new_pair(a1, args).unwrap(); + // ((a1 a2)) + args = allocator.new_pair(args, allocator.nil()).unwrap(); + let result = op_raise(&mut allocator, args, 100000); + assert_eq!(result, Err(EvalErr(args, "clvm raise".to_string()))); + } -#[test] -fn test_single_argument_raise_pair() { - let mut allocator = Allocator::new(); - let a1 = allocator.new_atom(&[65]).unwrap(); - let a2 = allocator.new_atom(&[66]).unwrap(); - // (a2) - let mut args = allocator.new_pair(a2, allocator.nil()).unwrap(); - // (a1 a2) - args = allocator.new_pair(a1, args).unwrap(); - // ((a1 a2)) - args = allocator.new_pair(args, allocator.nil()).unwrap(); - let result = op_raise(&mut allocator, args, 100000); - assert_eq!(result, Err(EvalErr(args, "clvm raise".to_string()))); -} + #[test] + fn test_multi_argument_raise() { + let mut allocator = Allocator::new(); + let a1 = allocator.new_atom(&[65]).unwrap(); + let a2 = allocator.new_atom(&[66]).unwrap(); + // (a1) + let mut args = allocator.new_pair(a2, allocator.nil()).unwrap(); + // (a1 a2) + args = allocator.new_pair(a1, args).unwrap(); + let result = op_raise(&mut allocator, args, 100000); + assert_eq!(result, Err(EvalErr(args, "clvm raise".to_string()))); + } -#[test] -fn test_multi_argument_raise() { - let mut allocator = Allocator::new(); - let a1 = allocator.new_atom(&[65]).unwrap(); - let a2 = allocator.new_atom(&[66]).unwrap(); - // (a1) - let mut args = allocator.new_pair(a2, allocator.nil()).unwrap(); - // (a1 a2) - args = allocator.new_pair(a1, args).unwrap(); - let result = op_raise(&mut allocator, args, 100000); - assert_eq!(result, Err(EvalErr(args, "clvm raise".to_string()))); -} + #[cfg(feature = "pre-eval")] + const COST_LIMIT: u64 = 1000000000; -#[cfg(feature = "pre-eval")] -const COST_LIMIT: u64 = 1000000000; + #[cfg(feature = "pre-eval")] + struct EvalFTracker { + pub prog: NodePtr, + pub args: NodePtr, + pub outcome: Option, + } -#[cfg(feature = "pre-eval")] -struct EvalFTracker { - pub prog: NodePtr, - pub args: NodePtr, - pub outcome: Option, -} + #[cfg(feature = "pre-eval")] + type Callback = Box)>; + + #[cfg(feature = "pre-eval")] + type PreEvalF = + Box Result, EvalErr>>; + + // Ensure pre_eval_f and post_eval_f are working as expected. + #[cfg(feature = "pre-eval")] + #[test] + fn test_pre_eval_and_post_eval() { + let mut allocator = Allocator::new(); + + let a1 = allocator.new_atom(&[1]).unwrap(); + let a2 = allocator.new_atom(&[2]).unwrap(); + let a4 = allocator.new_atom(&[4]).unwrap(); + let a5 = allocator.new_atom(&[5]).unwrap(); + + let a99 = allocator.new_atom(&[99]).unwrap(); + let a101 = allocator.new_atom(&[101]).unwrap(); + + // (a (q . (f (c 2 5))) (q 99 101)) + let arg_tail = allocator.new_pair(a101, allocator.nil()).unwrap(); + let arg_mid = allocator.new_pair(a99, arg_tail).unwrap(); + let args = allocator.new_pair(a1, arg_mid).unwrap(); + + let cons_tail = allocator.new_pair(a5, allocator.nil()).unwrap(); + let cons_args = allocator.new_pair(a2, cons_tail).unwrap(); + let cons_expr = allocator.new_pair(a4, cons_args).unwrap(); + + let f_tail = allocator.new_pair(cons_expr, allocator.nil()).unwrap(); + let f_expr = allocator.new_pair(a5, f_tail).unwrap(); + let f_quoted = allocator.new_pair(a1, f_expr).unwrap(); + + let a_tail = allocator.new_pair(args, allocator.nil()).unwrap(); + let a_args = allocator.new_pair(f_quoted, a_tail).unwrap(); + let program = allocator.new_pair(a2, a_args).unwrap(); + + let tracking = Rc::new(RefCell::new(HashMap::new())); + let pre_eval_tracking = tracking.clone(); + let pre_eval_f: PreEvalF = Box::new(move |_allocator, prog, args| { + let tracking_key = pre_eval_tracking.borrow().len(); + // Ensure lifetime of mutable borrow is contained. + // It must end before the lifetime of the following closure. + { + let mut tracking_mutable = pre_eval_tracking.borrow_mut(); + tracking_mutable.insert( + tracking_key, + EvalFTracker { + prog, + args, + outcome: None, + }, + ); + } + let post_eval_tracking = pre_eval_tracking.clone(); + let post_eval_f: Callback = Box::new(move |_a, outcome| { + let mut tracking_mutable = post_eval_tracking.borrow_mut(); + tracking_mutable.insert( + tracking_key, + EvalFTracker { + prog, + args, + outcome, + }, + ); + }); + Ok(Some(post_eval_f)) + }); -#[cfg(feature = "pre-eval")] -use crate::chia_dialect::{ChiaDialect, NO_UNKNOWN_OPS}; -#[cfg(feature = "pre-eval")] -use crate::run_program::run_program_with_pre_eval; -#[cfg(feature = "pre-eval")] -use std::cell::RefCell; -#[cfg(feature = "pre-eval")] -use std::collections::HashSet; - -// Allows move closures to tear off a reference and move it. // Allows interior -// mutability inside Fn traits. -#[cfg(feature = "pre-eval")] -use std::rc::Rc; - -#[cfg(feature = "pre-eval")] -type Callback = Box)>; - -#[cfg(feature = "pre-eval")] -type PreEvalF = Box Result, EvalErr>>; - -// Ensure pre_eval_f and post_eval_f are working as expected. -#[cfg(feature = "pre-eval")] -#[test] -fn test_pre_eval_and_post_eval() { - let mut allocator = Allocator::new(); - - let a1 = allocator.new_atom(&[1]).unwrap(); - let a2 = allocator.new_atom(&[2]).unwrap(); - let a4 = allocator.new_atom(&[4]).unwrap(); - let a5 = allocator.new_atom(&[5]).unwrap(); - - let a99 = allocator.new_atom(&[99]).unwrap(); - let a101 = allocator.new_atom(&[101]).unwrap(); - - // (a (q . (f (c 2 5))) (q 99 101)) - let arg_tail = allocator.new_pair(a101, allocator.nil()).unwrap(); - let arg_mid = allocator.new_pair(a99, arg_tail).unwrap(); - let args = allocator.new_pair(a1, arg_mid).unwrap(); - - let cons_tail = allocator.new_pair(a5, allocator.nil()).unwrap(); - let cons_args = allocator.new_pair(a2, cons_tail).unwrap(); - let cons_expr = allocator.new_pair(a4, cons_args).unwrap(); - - let f_tail = allocator.new_pair(cons_expr, allocator.nil()).unwrap(); - let f_expr = allocator.new_pair(a5, f_tail).unwrap(); - let f_quoted = allocator.new_pair(a1, f_expr).unwrap(); - - let a_tail = allocator.new_pair(args, allocator.nil()).unwrap(); - let a_args = allocator.new_pair(f_quoted, a_tail).unwrap(); - let program = allocator.new_pair(a2, a_args).unwrap(); - - let tracking = Rc::new(RefCell::new(HashMap::new())); - let pre_eval_tracking = tracking.clone(); - let pre_eval_f: PreEvalF = Box::new(move |_allocator, prog, args| { - let tracking_key = pre_eval_tracking.borrow().len(); - // Ensure lifetime of mutable borrow is contained. - // It must end before the lifetime of the following closure. - { - let mut tracking_mutable = pre_eval_tracking.borrow_mut(); - tracking_mutable.insert( - tracking_key, - EvalFTracker { - prog, - args, - outcome: None, - }, - ); + let result = run_program_with_pre_eval( + &mut allocator, + &ChiaDialect::new(NO_UNKNOWN_OPS), + program, + NodePtr::NIL, + COST_LIMIT, + Some(pre_eval_f), + ) + .unwrap(); + + assert!(node_eq(&allocator, result.1, a99)); + + // Should produce these: + // (q 99 101) => (99 101) + // (q . (f (c 2 5))) => (f (c 2 5)) + // 2 (99 101) => 99 + // 5 (99 101) => 101 + // (c 2 5) (99 101) => (99 . 101) + // (f (c 2 5)) (99 101) => 99 + // (a (q 5 (c 2 5))) () => 99 + + // args consed + let args_consed = allocator.new_pair(a99, a101).unwrap(); + + let desired_outcomes = [ + (args, NodePtr::NIL, arg_mid), + (f_quoted, NodePtr::NIL, f_expr), + (a2, arg_mid, a99), + (a5, arg_mid, a101), + (cons_expr, arg_mid, args_consed), + (f_expr, arg_mid, a99), + (program, NodePtr::NIL, a99), + ]; + + let mut found_outcomes = HashSet::new(); + let tracking_examine = tracking.borrow(); + for (_, v) in tracking_examine.iter() { + let found = desired_outcomes.iter().position(|(p, a, o)| { + node_eq(&allocator, *p, v.prog) + && node_eq(&allocator, *a, v.args) + && node_eq(&allocator, v.outcome.unwrap(), *o) + }); + found_outcomes.insert(found); + assert!(found.is_some()); } - let post_eval_tracking = pre_eval_tracking.clone(); - let post_eval_f: Callback = Box::new(move |_a, outcome| { - let mut tracking_mutable = post_eval_tracking.borrow_mut(); - tracking_mutable.insert( - tracking_key, - EvalFTracker { - prog, - args, - outcome, - }, - ); - }); - Ok(Some(post_eval_f)) - }); - - let result = run_program_with_pre_eval( - &mut allocator, - &ChiaDialect::new(NO_UNKNOWN_OPS), - program, - NodePtr::NIL, - COST_LIMIT, - Some(pre_eval_f), - ) - .unwrap(); - - assert!(node_eq(&allocator, result.1, a99)); - - // Should produce these: - // (q 99 101) => (99 101) - // (q . (f (c 2 5))) => (f (c 2 5)) - // 2 (99 101) => 99 - // 5 (99 101) => 101 - // (c 2 5) (99 101) => (99 . 101) - // (f (c 2 5)) (99 101) => 99 - // (a (q 5 (c 2 5))) () => 99 - - // args consed - let args_consed = allocator.new_pair(a99, a101).unwrap(); - - let desired_outcomes = [ - (args, NodePtr::NIL, arg_mid), - (f_quoted, NodePtr::NIL, f_expr), - (a2, arg_mid, a99), - (a5, arg_mid, a101), - (cons_expr, arg_mid, args_consed), - (f_expr, arg_mid, a99), - (program, NodePtr::NIL, a99), - ]; - - let mut found_outcomes = HashSet::new(); - let tracking_examine = tracking.borrow(); - for (_, v) in tracking_examine.iter() { - let found = desired_outcomes.iter().position(|(p, a, o)| { - node_eq(&allocator, *p, v.prog) - && node_eq(&allocator, *a, v.args) - && node_eq(&allocator, v.outcome.unwrap(), *o) - }); - found_outcomes.insert(found); - assert!(found.is_some()); - } - assert_eq!(tracking_examine.len(), desired_outcomes.len()); - assert_eq!(tracking_examine.len(), found_outcomes.len()); + assert_eq!(tracking_examine.len(), desired_outcomes.len()); + assert_eq!(tracking_examine.len(), found_outcomes.len()); + } } diff --git a/src/tests.rs b/src/tests.rs index 756098ee..f05eeb7c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,8 +1,6 @@ use super::allocator::{Allocator, NodePtr}; use super::serde::node_from_bytes; use super::serde::node_to_bytes; - -#[cfg(test)] use super::test_ops::node_eq; fn test_serialize_roundtrip(a: &mut Allocator, n: NodePtr) { diff --git a/src/traverse_path.rs b/src/traverse_path.rs index fa47b0dc..9e2634c1 100644 --- a/src/traverse_path.rs +++ b/src/traverse_path.rs @@ -108,155 +108,160 @@ pub fn traverse_path_fast(allocator: &Allocator, mut node_index: u32, args: Node Ok(Reduction(cost, arg_list)) } -#[test] -fn test_msb_mask() { - assert_eq!(msb_mask(0x0), 0x0); - assert_eq!(msb_mask(0x01), 0x01); - assert_eq!(msb_mask(0x02), 0x02); - assert_eq!(msb_mask(0x04), 0x04); - assert_eq!(msb_mask(0x08), 0x08); - assert_eq!(msb_mask(0x10), 0x10); - assert_eq!(msb_mask(0x20), 0x20); - assert_eq!(msb_mask(0x40), 0x40); - assert_eq!(msb_mask(0x80), 0x80); - - assert_eq!(msb_mask(0x44), 0x40); - assert_eq!(msb_mask(0x2a), 0x20); - assert_eq!(msb_mask(0xff), 0x80); - assert_eq!(msb_mask(0x0f), 0x08); -} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_msb_mask() { + assert_eq!(msb_mask(0x0), 0x0); + assert_eq!(msb_mask(0x01), 0x01); + assert_eq!(msb_mask(0x02), 0x02); + assert_eq!(msb_mask(0x04), 0x04); + assert_eq!(msb_mask(0x08), 0x08); + assert_eq!(msb_mask(0x10), 0x10); + assert_eq!(msb_mask(0x20), 0x20); + assert_eq!(msb_mask(0x40), 0x40); + assert_eq!(msb_mask(0x80), 0x80); + + assert_eq!(msb_mask(0x44), 0x40); + assert_eq!(msb_mask(0x2a), 0x20); + assert_eq!(msb_mask(0xff), 0x80); + assert_eq!(msb_mask(0x0f), 0x08); + } -#[test] -fn test_first_non_zero() { - assert_eq!(first_non_zero(&[]), 0); - assert_eq!(first_non_zero(&[1]), 0); - assert_eq!(first_non_zero(&[0]), 1); - assert_eq!(first_non_zero(&[0, 0, 0, 1, 1, 1]), 3); - assert_eq!(first_non_zero(&[0, 0, 0, 0, 0, 0]), 6); - assert_eq!(first_non_zero(&[1, 0, 0, 0, 0, 0]), 0); -} + #[test] + fn test_first_non_zero() { + assert_eq!(first_non_zero(&[]), 0); + assert_eq!(first_non_zero(&[1]), 0); + assert_eq!(first_non_zero(&[0]), 1); + assert_eq!(first_non_zero(&[0, 0, 0, 1, 1, 1]), 3); + assert_eq!(first_non_zero(&[0, 0, 0, 0, 0, 0]), 6); + assert_eq!(first_non_zero(&[1, 0, 0, 0, 0, 0]), 0); + } -#[test] -fn test_traverse_path() { - use crate::allocator::Allocator; - - let mut a = Allocator::new(); - let nul = a.nil(); - let n1 = a.new_atom(&[0, 1, 2]).unwrap(); - let n2 = a.new_atom(&[4, 5, 6]).unwrap(); - - assert_eq!(traverse_path(&a, &[], n1).unwrap(), Reduction(44, nul)); - assert_eq!(traverse_path(&a, &[0b1], n1).unwrap(), Reduction(44, n1)); - assert_eq!(traverse_path(&a, &[0b1], n2).unwrap(), Reduction(44, n2)); - - // cost for leading zeros - assert_eq!(traverse_path(&a, &[0], n1).unwrap(), Reduction(48, nul)); - assert_eq!(traverse_path(&a, &[0, 0], n1).unwrap(), Reduction(52, nul)); - assert_eq!( - traverse_path(&a, &[0, 0, 0], n1).unwrap(), - Reduction(56, nul) - ); - assert_eq!( - traverse_path(&a, &[0, 0, 0, 0], n1).unwrap(), - Reduction(60, nul) - ); - - let n3 = a.new_pair(n1, n2).unwrap(); - assert_eq!(traverse_path(&a, &[0b1], n3).unwrap(), Reduction(44, n3)); - assert_eq!(traverse_path(&a, &[0b10], n3).unwrap(), Reduction(48, n1)); - assert_eq!(traverse_path(&a, &[0b11], n3).unwrap(), Reduction(48, n2)); - assert_eq!(traverse_path(&a, &[0b11], n3).unwrap(), Reduction(48, n2)); - - let list = a.new_pair(n1, nul).unwrap(); - let list = a.new_pair(n2, list).unwrap(); - - assert_eq!(traverse_path(&a, &[0b10], list).unwrap(), Reduction(48, n2)); - assert_eq!( - traverse_path(&a, &[0b101], list).unwrap(), - Reduction(52, n1) - ); - assert_eq!( - traverse_path(&a, &[0b111], list).unwrap(), - Reduction(52, nul) - ); - - // errors - assert_eq!( - traverse_path(&a, &[0b1011], list).unwrap_err(), - EvalErr(nul, "path into atom".to_string()) - ); - assert_eq!( - traverse_path(&a, &[0b1101], list).unwrap_err(), - EvalErr(n1, "path into atom".to_string()) - ); - assert_eq!( - traverse_path(&a, &[0b1001], list).unwrap_err(), - EvalErr(n1, "path into atom".to_string()) - ); - assert_eq!( - traverse_path(&a, &[0b1010], list).unwrap_err(), - EvalErr(n2, "path into atom".to_string()) - ); - assert_eq!( - traverse_path(&a, &[0b1110], list).unwrap_err(), - EvalErr(n2, "path into atom".to_string()) - ); -} + #[test] + fn test_traverse_path() { + use crate::allocator::Allocator; + + let mut a = Allocator::new(); + let nul = a.nil(); + let n1 = a.new_atom(&[0, 1, 2]).unwrap(); + let n2 = a.new_atom(&[4, 5, 6]).unwrap(); + + assert_eq!(traverse_path(&a, &[], n1).unwrap(), Reduction(44, nul)); + assert_eq!(traverse_path(&a, &[0b1], n1).unwrap(), Reduction(44, n1)); + assert_eq!(traverse_path(&a, &[0b1], n2).unwrap(), Reduction(44, n2)); + + // cost for leading zeros + assert_eq!(traverse_path(&a, &[0], n1).unwrap(), Reduction(48, nul)); + assert_eq!(traverse_path(&a, &[0, 0], n1).unwrap(), Reduction(52, nul)); + assert_eq!( + traverse_path(&a, &[0, 0, 0], n1).unwrap(), + Reduction(56, nul) + ); + assert_eq!( + traverse_path(&a, &[0, 0, 0, 0], n1).unwrap(), + Reduction(60, nul) + ); + + let n3 = a.new_pair(n1, n2).unwrap(); + assert_eq!(traverse_path(&a, &[0b1], n3).unwrap(), Reduction(44, n3)); + assert_eq!(traverse_path(&a, &[0b10], n3).unwrap(), Reduction(48, n1)); + assert_eq!(traverse_path(&a, &[0b11], n3).unwrap(), Reduction(48, n2)); + assert_eq!(traverse_path(&a, &[0b11], n3).unwrap(), Reduction(48, n2)); + + let list = a.new_pair(n1, nul).unwrap(); + let list = a.new_pair(n2, list).unwrap(); + + assert_eq!(traverse_path(&a, &[0b10], list).unwrap(), Reduction(48, n2)); + assert_eq!( + traverse_path(&a, &[0b101], list).unwrap(), + Reduction(52, n1) + ); + assert_eq!( + traverse_path(&a, &[0b111], list).unwrap(), + Reduction(52, nul) + ); + + // errors + assert_eq!( + traverse_path(&a, &[0b1011], list).unwrap_err(), + EvalErr(nul, "path into atom".to_string()) + ); + assert_eq!( + traverse_path(&a, &[0b1101], list).unwrap_err(), + EvalErr(n1, "path into atom".to_string()) + ); + assert_eq!( + traverse_path(&a, &[0b1001], list).unwrap_err(), + EvalErr(n1, "path into atom".to_string()) + ); + assert_eq!( + traverse_path(&a, &[0b1010], list).unwrap_err(), + EvalErr(n2, "path into atom".to_string()) + ); + assert_eq!( + traverse_path(&a, &[0b1110], list).unwrap_err(), + EvalErr(n2, "path into atom".to_string()) + ); + } -#[test] -fn test_traverse_path_fast_fast() { - use crate::allocator::Allocator; - - let mut a = Allocator::new(); - let nul = a.nil(); - let n1 = a.new_atom(&[0, 1, 2]).unwrap(); - let n2 = a.new_atom(&[4, 5, 6]).unwrap(); - - assert_eq!(traverse_path_fast(&a, 0, n1).unwrap(), Reduction(44, nul)); - assert_eq!(traverse_path_fast(&a, 0b1, n1).unwrap(), Reduction(44, n1)); - assert_eq!(traverse_path_fast(&a, 0b1, n2).unwrap(), Reduction(44, n2)); - - let n3 = a.new_pair(n1, n2).unwrap(); - assert_eq!(traverse_path_fast(&a, 0b1, n3).unwrap(), Reduction(44, n3)); - assert_eq!(traverse_path_fast(&a, 0b10, n3).unwrap(), Reduction(48, n1)); - assert_eq!(traverse_path_fast(&a, 0b11, n3).unwrap(), Reduction(48, n2)); - assert_eq!(traverse_path_fast(&a, 0b11, n3).unwrap(), Reduction(48, n2)); - - let list = a.new_pair(n1, nul).unwrap(); - let list = a.new_pair(n2, list).unwrap(); - - assert_eq!( - traverse_path_fast(&a, 0b10, list).unwrap(), - Reduction(48, n2) - ); - assert_eq!( - traverse_path_fast(&a, 0b101, list).unwrap(), - Reduction(52, n1) - ); - assert_eq!( - traverse_path_fast(&a, 0b111, list).unwrap(), - Reduction(52, nul) - ); - - // errors - assert_eq!( - traverse_path_fast(&a, 0b1011, list).unwrap_err(), - EvalErr(nul, "path into atom".to_string()) - ); - assert_eq!( - traverse_path_fast(&a, 0b1101, list).unwrap_err(), - EvalErr(n1, "path into atom".to_string()) - ); - assert_eq!( - traverse_path_fast(&a, 0b1001, list).unwrap_err(), - EvalErr(n1, "path into atom".to_string()) - ); - assert_eq!( - traverse_path_fast(&a, 0b1010, list).unwrap_err(), - EvalErr(n2, "path into atom".to_string()) - ); - assert_eq!( - traverse_path_fast(&a, 0b1110, list).unwrap_err(), - EvalErr(n2, "path into atom".to_string()) - ); + #[test] + fn test_traverse_path_fast_fast() { + use crate::allocator::Allocator; + + let mut a = Allocator::new(); + let nul = a.nil(); + let n1 = a.new_atom(&[0, 1, 2]).unwrap(); + let n2 = a.new_atom(&[4, 5, 6]).unwrap(); + + assert_eq!(traverse_path_fast(&a, 0, n1).unwrap(), Reduction(44, nul)); + assert_eq!(traverse_path_fast(&a, 0b1, n1).unwrap(), Reduction(44, n1)); + assert_eq!(traverse_path_fast(&a, 0b1, n2).unwrap(), Reduction(44, n2)); + + let n3 = a.new_pair(n1, n2).unwrap(); + assert_eq!(traverse_path_fast(&a, 0b1, n3).unwrap(), Reduction(44, n3)); + assert_eq!(traverse_path_fast(&a, 0b10, n3).unwrap(), Reduction(48, n1)); + assert_eq!(traverse_path_fast(&a, 0b11, n3).unwrap(), Reduction(48, n2)); + assert_eq!(traverse_path_fast(&a, 0b11, n3).unwrap(), Reduction(48, n2)); + + let list = a.new_pair(n1, nul).unwrap(); + let list = a.new_pair(n2, list).unwrap(); + + assert_eq!( + traverse_path_fast(&a, 0b10, list).unwrap(), + Reduction(48, n2) + ); + assert_eq!( + traverse_path_fast(&a, 0b101, list).unwrap(), + Reduction(52, n1) + ); + assert_eq!( + traverse_path_fast(&a, 0b111, list).unwrap(), + Reduction(52, nul) + ); + + // errors + assert_eq!( + traverse_path_fast(&a, 0b1011, list).unwrap_err(), + EvalErr(nul, "path into atom".to_string()) + ); + assert_eq!( + traverse_path_fast(&a, 0b1101, list).unwrap_err(), + EvalErr(n1, "path into atom".to_string()) + ); + assert_eq!( + traverse_path_fast(&a, 0b1001, list).unwrap_err(), + EvalErr(n1, "path into atom".to_string()) + ); + assert_eq!( + traverse_path_fast(&a, 0b1010, list).unwrap_err(), + EvalErr(n2, "path into atom".to_string()) + ); + assert_eq!( + traverse_path_fast(&a, 0b1110, list).unwrap_err(), + EvalErr(n2, "path into atom".to_string()) + ); + } }