use crate::allocator::Allocator;
use crate::allocator::SExp::Atom;
use crate::cost::Cost;
use crate::err_utils::err;
use crate::node::Node;
use crate::number::Number;
use crate::reduction::EvalErr;
use crate::reduction::{Reduction, Response};
use bls12_381::Scalar;
use lazy_static::lazy_static;
use num_bigint::{BigUint, Sign};
use num_integer::Integer;
pub const MALLOC_COST_PER_BYTE: Cost = 10;
pub fn check_arg_count(args: &Node, expected: usize, name: &str) -> Result<(), EvalErr> {
if arg_count(args, expected) != expected {
args.err(&format!(
"{} takes exactly {} argument{}",
name,
expected,
if expected == 1 { "" } else { "s" }
))
} else {
Ok(())
}
}
pub fn arg_count(args: &Node, return_early_if_exceeds: usize) -> usize {
let mut count = 0;
let mut ptr = args.clone();
while let Some((_, next)) = ptr.pair() {
ptr = next.clone();
count += 1;
if count > return_early_if_exceeds {
break;
};
}
count
}
#[test]
fn test_arg_count() {
use crate::allocator::Allocator;
let mut allocator = Allocator::new();
let null = allocator.null();
let ptr_0_args = null;
let ptr_1_args = allocator.new_pair(null, ptr_0_args).unwrap();
let ptr_2_args = allocator.new_pair(null, ptr_1_args).unwrap();
let ptr_3_args = allocator.new_pair(null, ptr_2_args).unwrap();
let count_0_args: Node = Node::new(&allocator, ptr_0_args);
assert_eq!(arg_count(&count_0_args, 0), 0);
assert_eq!(arg_count(&count_0_args, 1), 0);
assert_eq!(arg_count(&count_0_args, 2), 0);
let count_1_args: Node = Node::new(&allocator, ptr_1_args);
assert_eq!(arg_count(&count_1_args, 0), 1);
assert_eq!(arg_count(&count_1_args, 1), 1);
assert_eq!(arg_count(&count_1_args, 2), 1);
let count_2_args: Node = Node::new(&allocator, ptr_2_args);
assert_eq!(arg_count(&count_2_args, 0), 1);
assert_eq!(arg_count(&count_2_args, 1), 2);
assert_eq!(arg_count(&count_2_args, 2), 2);
assert_eq!(arg_count(&count_2_args, 3), 2);
let count_3_args: Node = Node::new(&allocator, ptr_3_args);
assert_eq!(arg_count(&count_3_args, 0), 1);
assert_eq!(arg_count(&count_3_args, 1), 2);
assert_eq!(arg_count(&count_3_args, 2), 3);
assert_eq!(arg_count(&count_3_args, 3), 3);
assert_eq!(arg_count(&count_3_args, 4), 3);
}
pub fn int_atom(args: Node, op_name: &str) -> Result<(Number, usize), EvalErr> {
match args.sexp() {
Atom(_) => Ok((
args.allocator.number(args.node),
args.allocator.atom_len(args.node),
)),
_ => args.err(&format!("{op_name} requires int args")),
}
}
pub fn atom_len(args: Node, op_name: &str) -> Result<usize, EvalErr> {
match args.sexp() {
Atom(_) => Ok(args.allocator.atom_len(args.node)),
_ => args.err(&format!("{op_name} requires an atom")),
}
}
pub fn uint_atom<const SIZE: usize>(args: &Node, op_name: &str) -> Result<u64, EvalErr> {
let bytes = match args.atom() {
Some(a) => a,
_ => {
return args.err(&format!("{op_name} requires int arg"));
}
};
if bytes.is_empty() {
return Ok(0);
}
if (bytes[0] & 0x80) != 0 {
return args.err(&format!("{op_name} requires positive int arg"));
}
let mut buf: &[u8] = bytes;
while !buf.is_empty() && buf[0] == 0 {
buf = &buf[1..];
}
if buf.len() > SIZE {
return args.err(&format!("{op_name} requires u{} arg", SIZE * 8));
}
let mut ret = 0;
for b in buf {
ret <<= 8;
ret |= *b as u64;
}
Ok(ret)
}
#[cfg(test)]
use rstest::rstest;
#[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>(&Node::new(&a, n), "test") == Ok(expected));
}
#[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>(&Node::new(&a, n), "test") == Err(EvalErr(n, expected.to_string())));
}
#[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>(&Node::new(&a, p), "test")
== Err(EvalErr(p, "test requires int arg".to_string()))
);
}
#[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>(&Node::new(&a, n), "test") == Ok(expected));
}
#[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>(&Node::new(&a, n), "test") == Err(EvalErr(n, expected.to_string())));
}
#[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>(&Node::new(&a, p), "test")
== Err(EvalErr(p, "test requires int arg".to_string()))
);
}
pub fn atom<'a>(args: Node<'a>, op_name: &str) -> Result<&'a [u8], EvalErr> {
match args.atom() {
Some(a) => Ok(a),
_ => args.err(&format!("{op_name} on list")),
}
}
pub fn two_ints(args: &Node, op_name: &str) -> Result<(Number, usize, Number, usize), EvalErr> {
check_arg_count(args, 2, op_name)?;
let (n0, n0_len) = int_atom(args.first()?, op_name)?;
let (n1, n1_len) = int_atom(args.rest()?.first()?, op_name)?;
Ok((n0, n0_len, n1, n1_len))
}
fn u32_from_u8_impl(buf: &[u8], signed: bool) -> Option<u32> {
if buf.is_empty() {
return Some(0);
}
if buf.len() > 4 {
return None;
}
let sign_extend = (buf[0] & 0x80) != 0;
let mut ret: u32 = if signed && sign_extend { 0xffffffff } else { 0 };
for b in buf {
ret <<= 8;
ret |= *b as u32;
}
Some(ret)
}
pub fn u32_from_u8(buf: &[u8]) -> Option<u32> {
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));
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);
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<i32> {
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));
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);
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;
}
let mut ret: u64 = 0;
for b in buf {
ret <<= 8;
ret |= *b as 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(args: &Node, op_name: &str) -> Result<i32, EvalErr> {
let buf = match args.atom() {
Some(a) => a,
_ => {
return args.err(&format!("{op_name} requires int32 args"));
}
};
match i32_from_u8(buf) {
Some(v) => Ok(v),
_ => args.err(&format!(
"{op_name} requires int32 args (with no leading zeros)"
)),
}
}
impl<'a> Node<'a> {
pub fn first(&self) -> Result<Node<'a>, EvalErr> {
match self.pair() {
Some((p1, _)) => Ok(self.with_node(p1.node)),
_ => self.err("first of non-cons"),
}
}
pub fn rest(&self) -> Result<Node<'a>, EvalErr> {
match self.pair() {
Some((_, p2)) => Ok(self.with_node(p2.node)),
_ => self.err("rest of non-cons"),
}
}
pub fn err<T>(&self, msg: &str) -> Result<T, EvalErr> {
err(self.node, msg)
}
}
pub fn number_to_scalar(n: Number) -> Scalar {
let (sign, as_u8): (Sign, Vec<u8>) = n.to_bytes_le();
let mut scalar_array: [u8; 32] = [0; 32];
scalar_array[..as_u8.len()].clone_from_slice(&as_u8[..]);
let exp: Scalar = Scalar::from_bytes(&scalar_array).unwrap();
if sign == Sign::Minus {
exp.neg()
} else {
exp
}
}
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)?))
}
pub fn mod_group_order(n: Number) -> Number {
let order = GROUP_ORDER.clone();
let mut remainder = n.mod_floor(&order);
if remainder.sign() == Sign::Minus {
remainder += order;
}
remainder
}
lazy_static! {
static ref GROUP_ORDER: Number = {
let order_as_bytes = &[
0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1,
0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
];
let n = BigUint::from_bytes_be(order_as_bytes);
n.into()
};
}