use bls12_381::{G1Affine, G1Projective, Scalar};
use num_bigint::{BigUint, Sign};
use num_integer::Integer;
use std::ops::BitAndAssign;
use std::ops::BitOrAssign;
use std::ops::BitXorAssign;
use crate::allocator::{Allocator, NodePtr, SExp};
use crate::cost::{check_cost, Cost};
use crate::err_utils::err;
use crate::node::Node;
use crate::number::Number;
use crate::op_utils::{
arg_count, atom, atom_len, check_arg_count, i32_atom, int_atom, mod_group_order,
new_atom_and_cost, number_to_scalar, two_ints, u32_from_u8, MALLOC_COST_PER_BYTE,
};
use crate::reduction::{Reduction, Response};
use crate::sha2::{Digest, Sha256};
const ARITH_BASE_COST: Cost = 99;
const ARITH_COST_PER_ARG: Cost = 320;
const ARITH_COST_PER_BYTE: Cost = 3;
const LOG_BASE_COST: Cost = 100;
const LOG_COST_PER_ARG: Cost = 264;
const LOG_COST_PER_BYTE: Cost = 3;
const LOGNOT_BASE_COST: Cost = 331;
const LOGNOT_COST_PER_BYTE: Cost = 3;
const MUL_BASE_COST: Cost = 92;
const MUL_COST_PER_OP: Cost = 885;
const MUL_LINEAR_COST_PER_BYTE: Cost = 6;
const MUL_SQUARE_COST_PER_BYTE_DIVIDER: Cost = 128;
const GR_BASE_COST: Cost = 498;
const GR_COST_PER_BYTE: Cost = 2;
const GRS_BASE_COST: Cost = 117;
const GRS_COST_PER_BYTE: Cost = 1;
const STRLEN_BASE_COST: Cost = 173;
const STRLEN_COST_PER_BYTE: Cost = 1;
const CONCAT_BASE_COST: Cost = 142;
const CONCAT_COST_PER_ARG: Cost = 135;
const CONCAT_COST_PER_BYTE: Cost = 3;
const DIVMOD_BASE_COST: Cost = 1116;
const DIVMOD_COST_PER_BYTE: Cost = 6;
const DIV_BASE_COST: Cost = 988;
const DIV_COST_PER_BYTE: Cost = 4;
const SHA256_BASE_COST: Cost = 87;
const SHA256_COST_PER_ARG: Cost = 134;
const SHA256_COST_PER_BYTE: Cost = 2;
const ASHIFT_BASE_COST: Cost = 596;
const ASHIFT_COST_PER_BYTE: Cost = 3;
const LSHIFT_BASE_COST: Cost = 277;
const LSHIFT_COST_PER_BYTE: Cost = 3;
const BOOL_BASE_COST: Cost = 200;
const BOOL_COST_PER_ARG: Cost = 300;
const POINT_ADD_BASE_COST: Cost = 101094;
const POINT_ADD_COST_PER_ARG: Cost = 1343980;
const PUBKEY_BASE_COST: Cost = 1325730;
const PUBKEY_COST_PER_BYTE: Cost = 38;
const COINID_COST: Cost =
SHA256_BASE_COST + SHA256_COST_PER_ARG * 3 + SHA256_COST_PER_BYTE * (32 + 32 + 8) - 153;
fn limbs_for_int(v: &Number) -> usize {
((v.bits() + 7) / 8) as usize
}
#[cfg(test)]
fn limb_test_helper(bytes: &[u8]) {
let bigint = Number::from_signed_bytes_be(&bytes);
println!("{} bits: {}", &bigint, &bigint.bits());
let expected = if bytes.len() > 0 && bytes[0] == 0 {
bytes.len() - 1
} else {
bytes.len()
};
assert_eq!(limbs_for_int(&bigint), expected);
}
#[test]
fn test_limbs_for_int() {
limb_test_helper(&[]);
limb_test_helper(&[0x1]);
limb_test_helper(&[0x80]);
limb_test_helper(&[0x81]);
limb_test_helper(&[0x7f]);
limb_test_helper(&[0xff]);
limb_test_helper(&[0, 0xff]);
limb_test_helper(&[0x7f, 0xff]);
limb_test_helper(&[0x7f, 0]);
limb_test_helper(&[0x7f, 0x77]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x40, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x20, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x10, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x08, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x04, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x02, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x01, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0, 0, 0]);
limb_test_helper(&[0x80, 0, 0, 0, 0, 0, 0, 0]);
}
fn malloc_cost(a: &Allocator, cost: Cost, ptr: NodePtr) -> Reduction {
let c = a.atom_len(ptr) as Cost * MALLOC_COST_PER_BYTE;
Reduction(cost + c, ptr)
}
pub fn op_unknown(
allocator: &mut Allocator,
o: NodePtr,
args: NodePtr,
max_cost: Cost,
) -> Response {
let op = allocator.atom(o);
if op.is_empty() || (op.len() >= 2 && op[0] == 0xff && op[1] == 0xff) {
return err(o, "reserved operator");
}
let cost_function = (op[op.len() - 1] & 0b11000000) >> 6;
let cost_multiplier: u64 = match u32_from_u8(&op[0..op.len() - 1]) {
Some(v) => v as u64,
None => {
return err(o, "invalid operator");
}
};
let mut cost = match cost_function {
0 => 1,
1 => {
let mut cost = ARITH_BASE_COST;
let mut byte_count: u64 = 0;
for arg in Node::new(allocator, args) {
cost += ARITH_COST_PER_ARG;
let len = atom_len(arg, "unknown op")?;
byte_count += len as u64;
check_cost(
allocator,
cost + (byte_count as Cost * ARITH_COST_PER_BYTE),
max_cost,
)?;
}
cost + (byte_count * ARITH_COST_PER_BYTE)
}
2 => {
let mut cost = MUL_BASE_COST;
let mut first_iter: bool = true;
let mut l0: u64 = 0;
for arg in Node::new(allocator, args) {
let len = atom_len(arg, "unknown op")?;
if first_iter {
l0 = len as u64;
first_iter = false;
continue;
}
let l1 = len as u64;
cost += MUL_COST_PER_OP;
cost += (l0 + l1) * MUL_LINEAR_COST_PER_BYTE;
cost += (l0 * l1) / MUL_SQUARE_COST_PER_BYTE_DIVIDER;
l0 += l1;
check_cost(allocator, cost, max_cost)?;
}
cost
}
3 => {
let mut cost = CONCAT_BASE_COST;
let mut total_size: u64 = 0;
for arg in Node::new(allocator, args) {
cost += CONCAT_COST_PER_ARG;
let len = atom_len(arg, "unknown op")?;
total_size += len as u64;
check_cost(
allocator,
cost + total_size as Cost * CONCAT_COST_PER_BYTE,
max_cost,
)?;
}
cost + total_size * CONCAT_COST_PER_BYTE
}
_ => 1,
};
assert!(cost > 0);
check_cost(allocator, cost, max_cost)?;
cost *= cost_multiplier + 1;
if cost > u32::MAX as u64 {
err(o, "invalid operator")
} else {
Ok(Reduction(cost as Cost, allocator.null()))
}
}
#[cfg(test)]
fn test_op_unknown(buf: &[u8], a: &mut Allocator, n: NodePtr) -> Response {
let buf = a.new_atom(buf)?;
op_unknown(a, buf, n, 1000000)
}
#[test]
fn test_unknown_op_reserved() {
let mut a = Allocator::new();
let buf = vec![0xff, 0xff];
let null = a.null();
assert!(!test_op_unknown(&buf, &mut a, null).is_ok());
let buf = vec![0xff, 0xff, 0xff];
assert!(!test_op_unknown(&buf, &mut a, null).is_ok());
let buf = vec![0xff, 0xff, b'0'];
assert!(!test_op_unknown(&buf, &mut a, null).is_ok());
let buf = vec![0xff, 0xff, 0];
assert!(!test_op_unknown(&buf, &mut a, null).is_ok());
let buf = vec![0xff, 0xff, 0xcc, 0xcc, 0xfe, 0xed, 0xce];
assert!(!test_op_unknown(&buf, &mut a, null).is_ok());
let buf = Vec::<u8>::new();
assert!(!test_op_unknown(&buf, &mut a, null).is_ok());
let buf = vec![0xff];
assert_eq!(
test_op_unknown(&buf, &mut a, null),
Ok(Reduction(142, null))
);
let buf = vec![0x00, 0xff, 0xff, 0x00, 0x00];
assert_eq!(
test_op_unknown(&buf, &mut a, null),
Ok(Reduction(16776961, null))
);
}
#[test]
fn test_lenient_mode_last_bits() {
let mut a = crate::allocator::Allocator::new();
let buf = vec![0x3c, 0x3f];
let null = a.null();
assert_eq!(test_op_unknown(&buf, &mut a, null), Ok(Reduction(61, null)));
let buf = vec![0x3c, 0x0f];
assert_eq!(test_op_unknown(&buf, &mut a, null), Ok(Reduction(61, null)));
let buf = vec![0x3c, 0x00];
assert_eq!(test_op_unknown(&buf, &mut a, null), Ok(Reduction(61, null)));
let buf = vec![0x3c, 0x2c];
assert_eq!(test_op_unknown(&buf, &mut a, null), Ok(Reduction(61, null)));
}
pub fn op_sha256(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let mut cost = SHA256_BASE_COST;
let mut byte_count: usize = 0;
let mut hasher = Sha256::new();
for arg in Node::new(a, input) {
cost += SHA256_COST_PER_ARG;
check_cost(
a,
cost + byte_count as Cost * SHA256_COST_PER_BYTE,
max_cost,
)?;
let blob = atom(arg, "sha256")?;
byte_count += blob.len();
hasher.update(blob);
}
cost += byte_count as Cost * SHA256_COST_PER_BYTE;
new_atom_and_cost(a, cost, &hasher.finalize())
}
pub fn op_add(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let mut cost = ARITH_BASE_COST;
let mut byte_count: usize = 0;
let mut total: Number = 0.into();
for arg in Node::new(a, input) {
cost += ARITH_COST_PER_ARG;
check_cost(
a,
cost + (byte_count as Cost * ARITH_COST_PER_BYTE),
max_cost,
)?;
let (v, len) = int_atom(arg, "+")?;
byte_count += len;
total += v;
}
let total = a.new_number(total)?;
cost += byte_count as Cost * ARITH_COST_PER_BYTE;
Ok(malloc_cost(a, cost, total))
}
pub fn op_subtract(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let mut cost = ARITH_BASE_COST;
let mut byte_count: usize = 0;
let mut total: Number = 0.into();
let mut is_first = true;
for arg in Node::new(a, input) {
cost += ARITH_COST_PER_ARG;
check_cost(a, cost + byte_count as Cost * ARITH_COST_PER_BYTE, max_cost)?;
let (v, len) = int_atom(arg, "-")?;
byte_count += len;
if is_first {
total += v;
} else {
total -= v;
};
is_first = false;
}
let total = a.new_number(total)?;
cost += byte_count as Cost * ARITH_COST_PER_BYTE;
Ok(malloc_cost(a, cost, total))
}
pub fn op_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let mut cost: Cost = MUL_BASE_COST;
let mut first_iter: bool = true;
let mut total: Number = 1.into();
let mut l0: usize = 0;
for arg in Node::new(a, input) {
check_cost(a, cost, max_cost)?;
if first_iter {
(total, l0) = int_atom(arg, "*")?;
first_iter = false;
continue;
}
let (v0, l1) = int_atom(arg, "*")?;
total *= v0;
cost += MUL_COST_PER_OP;
cost += (l0 + l1) as Cost * MUL_LINEAR_COST_PER_BYTE;
cost += (l0 * l1) as Cost / MUL_SQUARE_COST_PER_BYTE_DIVIDER;
l0 = limbs_for_int(&total);
}
let total = a.new_number(total)?;
Ok(malloc_cost(a, cost, total))
}
pub fn op_div(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
let (a0, l0, a1, l1) = two_ints(&args, "/")?;
let cost = DIV_BASE_COST + ((l0 + l1) as Cost) * DIV_COST_PER_BYTE;
if a1.sign() == Sign::NoSign {
args.first()?.err("div with 0")
} else {
if a0.sign() == Sign::Minus || a1.sign() == Sign::Minus {
return args.err("div operator with negative operands is deprecated");
}
let (mut q, r) = a0.div_mod_floor(&a1);
if q == (-1).into() && r != 0.into() {
q += 1;
}
let q1 = a.new_number(q)?;
Ok(malloc_cost(a, cost, q1))
}
}
pub fn op_divmod(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
let (a0, l0, a1, l1) = two_ints(&args, "divmod")?;
let cost = DIVMOD_BASE_COST + ((l0 + l1) as Cost) * DIVMOD_COST_PER_BYTE;
if a1.sign() == Sign::NoSign {
args.first()?.err("divmod with 0")
} else {
let (q, r) = a0.div_mod_floor(&a1);
let q1 = a.new_number(q)?;
let r1 = a.new_number(r)?;
let c = (a.atom(q1).len() + a.atom(r1).len()) as Cost * MALLOC_COST_PER_BYTE;
let r: NodePtr = a.new_pair(q1, r1)?;
Ok(Reduction(cost + c, r))
}
}
pub fn op_gr(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 2, ">")?;
let (v0, v0_len) = int_atom(args.first()?, ">")?;
let (v1, v1_len) = int_atom(args.rest()?.first()?, ">")?;
let cost = GR_BASE_COST + (v0_len + v1_len) as Cost * GR_COST_PER_BYTE;
Ok(Reduction(cost, if v0 > v1 { a.one() } else { a.null() }))
}
pub fn op_gr_bytes(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 2, ">s")?;
let v0 = atom(args.first()?, ">s")?;
let v1 = atom(args.rest()?.first()?, ">s")?;
let cost = GRS_BASE_COST + (v0.len() + v1.len()) as Cost * GRS_COST_PER_BYTE;
Ok(Reduction(cost, if v0 > v1 { a.one() } else { a.null() }))
}
pub fn op_strlen(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 1, "strlen")?;
let size = atom_len(args.first()?, "strlen")?;
let size_node = a.new_number(size.into())?;
let cost = STRLEN_BASE_COST + size as Cost * STRLEN_COST_PER_BYTE;
Ok(malloc_cost(a, cost, size_node))
}
pub fn op_substr(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
let ac = arg_count(&args, 3);
if !(2..=3).contains(&ac) {
return args.err("substr takes exactly 2 or 3 arguments");
}
let a0 = args.first()?;
let s0 = atom(a0.clone(), "substr")?;
let size = s0.len();
let rest = args.rest()?;
let i1 = i32_atom(&rest.first()?, "substr")?;
let rest = rest.rest()?;
let i2 = if ac == 3 {
i32_atom(&rest.first()?, "substr")?
} else {
size as i32
};
if i2 < 0 || i1 < 0 || i2 as usize > size || i2 < i1 {
args.err("invalid indices for substr")
} else {
let atom_node = a0.node;
let r = a.new_substr(atom_node, i1 as u32, i2 as u32)?;
let cost: Cost = 1;
Ok(Reduction(cost, r))
}
}
pub fn op_concat(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let args = Node::new(a, input);
let mut cost = CONCAT_BASE_COST;
let mut total_size: usize = 0;
let mut terms = Vec::<NodePtr>::new();
for arg in &args {
cost += CONCAT_COST_PER_ARG;
check_cost(
a,
cost + total_size as Cost * CONCAT_COST_PER_BYTE,
max_cost,
)?;
match arg.sexp() {
SExp::Pair(_, _) => return arg.err("concat on list"),
SExp::Atom(b) => total_size += b.len(),
};
terms.push(arg.node);
}
cost += total_size as Cost * CONCAT_COST_PER_BYTE;
cost += total_size as Cost * MALLOC_COST_PER_BYTE;
check_cost(a, cost, max_cost)?;
let new_atom = a.new_concat(total_size, &terms)?;
Ok(Reduction(cost, new_atom))
}
pub fn op_ash(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 2, "ash")?;
let (i0, l0) = int_atom(args.first()?, "ash")?;
let rest = args.rest()?;
let a1 = i32_atom(&rest.first()?, "ash")?;
if !(-65535..=65535).contains(&a1) {
return args.rest()?.first()?.err("shift too large");
}
let v: Number = if a1 > 0 { i0 << a1 } else { i0 >> -a1 };
let l1 = limbs_for_int(&v);
let r = a.new_number(v)?;
let cost = ASHIFT_BASE_COST + ((l0 + l1) as Cost) * ASHIFT_COST_PER_BYTE;
Ok(malloc_cost(a, cost, r))
}
#[cfg(test)]
fn test_shift(
op: fn(&mut Allocator, NodePtr, Cost) -> Response,
a: &mut Allocator,
a1: &[u8],
a2: &[u8],
) -> Response {
let args = a.null();
let a2 = a.new_atom(a2).unwrap();
let args = a.new_pair(a2, args).unwrap();
let a1 = a.new_atom(a1).unwrap();
let args = a.new_pair(a1, args).unwrap();
op(a, args, 10000000 as Cost)
}
#[test]
fn test_op_ash() {
let mut a = Allocator::new();
assert_eq!(
test_shift(op_ash, &mut a, &[1], &[0x80, 0, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
assert_eq!(
test_shift(op_ash, &mut a, &[1], &[0x80, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
let node = test_shift(op_ash, &mut a, &[1], &[0x80, 0]).unwrap().1;
assert_eq!(a.atom(node), &[]);
assert_eq!(
test_shift(op_ash, &mut a, &[1], &[0x7f, 0, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
assert_eq!(
test_shift(op_ash, &mut a, &[1], &[0x7f, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
let node = test_shift(op_ash, &mut a, &[1], &[0x7f, 0]).unwrap().1;
let node = a.atom(node);
assert_eq!(node[0], 1);
assert_eq!(node.len(), 4065);
}
pub fn op_lsh(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 2, "lsh")?;
let b0 = atom(args.first()?, "lsh")?;
let i0 = BigUint::from_bytes_be(b0);
let l0 = b0.len();
let rest = args.rest()?;
let a1 = i32_atom(&rest.first()?, "lsh")?;
if !(-65535..=65535).contains(&a1) {
return args.rest()?.first()?.err("shift too large");
}
let i0: Number = i0.into();
let v: Number = if a1 > 0 { i0 << a1 } else { i0 >> -a1 };
let l1 = limbs_for_int(&v);
let r = a.new_number(v)?;
let cost = LSHIFT_BASE_COST + ((l0 + l1) as Cost) * LSHIFT_COST_PER_BYTE;
Ok(malloc_cost(a, cost, r))
}
#[test]
fn test_op_lsh() {
let mut a = Allocator::new();
assert_eq!(
test_shift(op_lsh, &mut a, &[1], &[0x80, 0, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
assert_eq!(
test_shift(op_lsh, &mut a, &[1], &[0x80, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
let node = test_shift(op_lsh, &mut a, &[1], &[0x80, 0]).unwrap().1;
assert_eq!(a.atom(node), &[]);
assert_eq!(
test_shift(op_lsh, &mut a, &[1], &[0x7f, 0, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
assert_eq!(
test_shift(op_lsh, &mut a, &[1], &[0x7f, 0, 0])
.unwrap_err()
.1,
"shift too large"
);
let node = test_shift(op_lsh, &mut a, &[1], &[0x7f, 0]).unwrap().1;
let node = a.atom(node);
assert_eq!(node[0], 1);
assert_eq!(node.len(), 4065);
}
fn binop_reduction(
op_name: &str,
a: &mut Allocator,
initial_value: Number,
input: NodePtr,
max_cost: Cost,
op_f: fn(&mut Number, &Number) -> (),
) -> Response {
let mut total = initial_value;
let mut arg_size: usize = 0;
let mut cost = LOG_BASE_COST;
for arg in Node::new(a, input) {
let (n0, len) = int_atom(arg, op_name)?;
op_f(&mut total, &n0);
arg_size += len;
cost += LOG_COST_PER_ARG;
check_cost(a, cost + (arg_size as Cost * LOG_COST_PER_BYTE), max_cost)?;
}
cost += arg_size as Cost * LOG_COST_PER_BYTE;
let total = a.new_number(total)?;
Ok(malloc_cost(a, cost, total))
}
fn logand_op(a: &mut Number, b: &Number) {
a.bitand_assign(b);
}
pub fn op_logand(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let v: Number = (-1).into();
binop_reduction("logand", a, v, input, max_cost, logand_op)
}
fn logior_op(a: &mut Number, b: &Number) {
a.bitor_assign(b);
}
pub fn op_logior(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let v: Number = (0).into();
binop_reduction("logior", a, v, input, max_cost, logior_op)
}
fn logxor_op(a: &mut Number, b: &Number) {
a.bitxor_assign(b);
}
pub fn op_logxor(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let v: Number = (0).into();
binop_reduction("logxor", a, v, input, max_cost, logxor_op)
}
pub fn op_lognot(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 1, "lognot")?;
let (mut n, len) = int_atom(args.first()?, "lognot")?;
n = !n;
let cost = LOGNOT_BASE_COST + ((len as Cost) * LOGNOT_COST_PER_BYTE);
let r = a.new_number(n)?;
Ok(malloc_cost(a, cost, r))
}
pub fn op_not(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 1, "not")?;
let r: NodePtr = args.from_bool(!args.first()?.as_bool()).node;
let cost = BOOL_BASE_COST;
Ok(Reduction(cost, r))
}
pub fn op_any(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let args = Node::new(a, input);
let mut cost = BOOL_BASE_COST;
let mut is_any = false;
for arg in &args {
cost += BOOL_COST_PER_ARG;
check_cost(a, cost, max_cost)?;
is_any = is_any || arg.as_bool();
}
let total: Node = args.from_bool(is_any);
Ok(Reduction(cost, total.node))
}
pub fn op_all(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let args = Node::new(a, input);
let mut cost = BOOL_BASE_COST;
let mut is_all = true;
for arg in &args {
cost += BOOL_COST_PER_ARG;
check_cost(a, cost, max_cost)?;
is_all = is_all && arg.as_bool();
}
let total: Node = args.from_bool(is_all);
Ok(Reduction(cost, total.node))
}
pub fn op_pubkey_for_exp(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 1, "pubkey_for_exp")?;
let (v0, v0_len) = int_atom(args.first()?, "pubkey_for_exp")?;
let exp: Number = mod_group_order(v0);
let cost = PUBKEY_BASE_COST + (v0_len as Cost) * PUBKEY_COST_PER_BYTE;
let exp: Scalar = number_to_scalar(exp);
let point: G1Projective = G1Affine::generator() * exp;
let point: G1Affine = point.into();
new_atom_and_cost(a, cost, &point.to_compressed())
}
pub fn op_point_add(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
let args = Node::new(a, input);
let mut cost = POINT_ADD_BASE_COST;
let mut total: G1Projective = G1Projective::identity();
for arg in &args {
let blob = atom(arg, "point_add")?;
let mut is_ok: bool = blob.len() == 48;
if is_ok {
let v = G1Affine::from_compressed(&blob.try_into().unwrap());
is_ok = v.is_some().into();
if is_ok {
let point = v.unwrap();
cost += POINT_ADD_COST_PER_ARG;
check_cost(a, cost, max_cost)?;
total += &point;
}
}
if !is_ok {
let blob: String = hex::encode(blob);
let msg = format!("point_add expects blob, got {blob}: Length of bytes object not equal to G1Element::SIZE");
return args.err(&msg);
}
}
let total: G1Affine = total.into();
new_atom_and_cost(a, cost, &total.to_compressed())
}
pub fn op_coinid(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response {
let args = Node::new(a, input);
check_arg_count(&args, 3, "coinid")?;
let parent_coin = atom(args.first()?, "coinid")?;
if parent_coin.len() != 32 {
return args.err("coinid: invalid parent coin id (must be 32 bytes)");
}
let args = args.rest()?;
let puzzle_hash = atom(args.first()?, "coinid")?;
if puzzle_hash.len() != 32 {
return args.err("coinid: invalid puzzle hash (must be 32 bytes)");
}
let args = args.rest()?;
let amount = atom(args.first()?, "coinid")?;
if !amount.is_empty() {
if (amount[0] & 0x80) != 0 {
return args.err("coinid: invalid amount (may not be negative");
}
if amount == [0_u8] || (amount.len() > 1 && amount[0] == 0 && (amount[1] & 0x80) == 0) {
return args.err("coinid: invalid amount (may not have redundant leading zero)");
}
if amount.len() > 9 || (amount.len() == 9 && amount[0] != 0) {
return args.err("coinid: invalid amount (may not exceed max coin amount)");
}
}
let mut hasher = Sha256::new();
hasher.update(parent_coin);
hasher.update(puzzle_hash);
hasher.update(amount);
let ret: [u8; 32] = hasher
.finalize()
.as_slice()
.try_into()
.expect("sha256 hash is not 32 bytes");
new_atom_and_cost(a, COINID_COST, &ret)
}