#[cfg(zisk_guest)]
use crate::alloc_extern::vec::Vec;
use crate::{
syscalls::{
syscall_bn254_curve_add, syscall_bn254_curve_dbl, SyscallBn254CurveAddParams,
SyscallPoint256,
},
zisklib::{eq, fcall_msb_pos_256, is_one, is_two, is_zero, lt},
};
use super::{
constants::{E_B, G1_IDENTITY, P},
fp::{add_fp_bn254, inv_fp_bn254, mul_fp_bn254, neg_fp_bn254, square_fp_bn254},
fr::reduce_fr_bn254,
};
#[allow(dead_code)]
pub(crate) const G1_ADD_SUCCESS: u8 = 0;
#[allow(dead_code)]
pub(crate) const G1_ADD_SUCCESS_INFINITY: u8 = 1;
const G1_ADD_ERR_NOT_IN_FIELD: u8 = 2;
const G1_ADD_ERR_NOT_ON_CURVE: u8 = 3;
#[allow(dead_code)]
pub(crate) const G1_MUL_SUCCESS: u8 = 0;
#[allow(dead_code)]
pub(crate) const G1_MUL_SUCCESS_INFINITY: u8 = 1;
const G1_MUL_ERR_NOT_IN_FIELD: u8 = 2;
const G1_MUL_ERR_NOT_ON_CURVE: u8 = 3;
pub fn jacobian_to_affine_bn254(
p: &[u64; 12],
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> [u64; 8] {
let z: [u64; 4] = p[8..12].try_into().unwrap();
if is_zero(&z) {
return G1_IDENTITY;
} else if is_one(&z) {
return [p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]];
}
let x: [u64; 4] = p[0..4].try_into().unwrap();
let y: [u64; 4] = p[4..8].try_into().unwrap();
let zinv = inv_fp_bn254(
&z,
#[cfg(feature = "hints")]
hints,
);
let zinv_sq = square_fp_bn254(
&zinv,
#[cfg(feature = "hints")]
hints,
);
let x_res = mul_fp_bn254(
&x,
&zinv_sq,
#[cfg(feature = "hints")]
hints,
);
let mut y_res = mul_fp_bn254(
&y,
&zinv_sq,
#[cfg(feature = "hints")]
hints,
);
y_res = mul_fp_bn254(
&y_res,
&zinv,
#[cfg(feature = "hints")]
hints,
);
[x_res[0], x_res[1], x_res[2], x_res[3], y_res[0], y_res[1], y_res[2], y_res[3]]
}
pub fn is_on_curve_bn254(p: &[u64; 8], #[cfg(feature = "hints")] hints: &mut Vec<u64>) -> bool {
let x: [u64; 4] = p[0..4].try_into().unwrap();
let y: [u64; 4] = p[4..8].try_into().unwrap();
let lhs = square_fp_bn254(
&y,
#[cfg(feature = "hints")]
hints,
);
let mut rhs = square_fp_bn254(
&x,
#[cfg(feature = "hints")]
hints,
);
rhs = mul_fp_bn254(
&rhs,
&x,
#[cfg(feature = "hints")]
hints,
);
rhs = add_fp_bn254(
&rhs,
&E_B,
#[cfg(feature = "hints")]
hints,
);
eq(&lhs, &rhs) || eq(p, &G1_IDENTITY)
}
pub fn add_bn254(
p1: &[u64; 8],
p2: &[u64; 8],
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> [u64; 8] {
let x1: [u64; 4] = p1[0..4].try_into().unwrap();
let y1: [u64; 4] = p1[4..8].try_into().unwrap();
let x2: [u64; 4] = p2[0..4].try_into().unwrap();
let y2: [u64; 4] = p2[4..8].try_into().unwrap();
if eq(&x1, &x2) {
if eq(&y1, &y2) {
return dbl_bn254(
p1,
#[cfg(feature = "hints")]
hints,
);
} else {
return G1_IDENTITY;
}
}
let mut p1 = SyscallPoint256 { x: x1, y: y1 };
let p2 = SyscallPoint256 { x: x2, y: y2 };
let mut params = SyscallBn254CurveAddParams { p1: &mut p1, p2: &p2 };
syscall_bn254_curve_add(
&mut params,
#[cfg(feature = "hints")]
hints,
);
let x3 = params.p1.x;
let y3 = params.p1.y;
[x3[0], x3[1], x3[2], x3[3], y3[0], y3[1], y3[2], y3[3]]
}
pub fn add_complete_safe_bn254(
p1: &[u64; 8],
p2: &[u64; 8],
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> Result<[u64; 8], u8> {
let p1_is_inf = eq(p1, &G1_IDENTITY);
let p2_is_inf = eq(p2, &G1_IDENTITY);
if p1_is_inf && p2_is_inf {
return Ok(G1_IDENTITY);
}
if p1_is_inf {
let x2: [u64; 4] = p2[0..4].try_into().unwrap();
let y2: [u64; 4] = p2[4..8].try_into().unwrap();
if !lt(&x2, &P) || !lt(&y2, &P) {
return Err(G1_ADD_ERR_NOT_IN_FIELD);
}
if !is_on_curve_bn254(
p2,
#[cfg(feature = "hints")]
hints,
) {
return Err(G1_ADD_ERR_NOT_ON_CURVE);
}
return Ok(*p2);
}
if p2_is_inf {
let x1: [u64; 4] = p1[0..4].try_into().unwrap();
let y1: [u64; 4] = p1[4..8].try_into().unwrap();
if !lt(&x1, &P) || !lt(&y1, &P) {
return Err(G1_ADD_ERR_NOT_IN_FIELD);
}
if !is_on_curve_bn254(
p1,
#[cfg(feature = "hints")]
hints,
) {
return Err(G1_ADD_ERR_NOT_ON_CURVE);
}
return Ok(*p1);
}
let x1: [u64; 4] = p1[0..4].try_into().unwrap();
let y1: [u64; 4] = p1[4..8].try_into().unwrap();
if !lt(&x1, &P) || !lt(&y1, &P) {
return Err(G1_ADD_ERR_NOT_IN_FIELD);
}
if !is_on_curve_bn254(
p1,
#[cfg(feature = "hints")]
hints,
) {
return Err(G1_ADD_ERR_NOT_ON_CURVE);
}
let x2: [u64; 4] = p2[0..4].try_into().unwrap();
let y2: [u64; 4] = p2[4..8].try_into().unwrap();
if !lt(&x2, &P) || !lt(&y2, &P) {
return Err(G1_ADD_ERR_NOT_IN_FIELD);
}
if !is_on_curve_bn254(
p2,
#[cfg(feature = "hints")]
hints,
) {
return Err(G1_ADD_ERR_NOT_ON_CURVE);
}
Ok(add_bn254(
p1,
p2,
#[cfg(feature = "hints")]
hints,
))
}
pub fn neg_bn254(p: &[u64; 8], #[cfg(feature = "hints")] hints: &mut Vec<u64>) -> [u64; 8] {
let x: [u64; 4] = p[0..4].try_into().unwrap();
let y: [u64; 4] = p[4..8].try_into().unwrap();
let y_neg = neg_fp_bn254(
&y,
#[cfg(feature = "hints")]
hints,
);
[x[0], x[1], x[2], x[3], y_neg[0], y_neg[1], y_neg[2], y_neg[3]]
}
pub fn dbl_bn254(p: &[u64; 8], #[cfg(feature = "hints")] hints: &mut Vec<u64>) -> [u64; 8] {
let mut p1 = SyscallPoint256 { x: p[0..4].try_into().unwrap(), y: p[4..8].try_into().unwrap() };
syscall_bn254_curve_dbl(
&mut p1,
#[cfg(feature = "hints")]
hints,
);
[p1.x[0], p1.x[1], p1.x[2], p1.x[3], p1.y[0], p1.y[1], p1.y[2], p1.y[3]]
}
pub fn scalar_mul_bn254(
p: &[u64; 8],
k: &[u64; 4],
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> [u64; 8] {
let k = reduce_fr_bn254(
k,
#[cfg(feature = "hints")]
hints,
);
if is_zero(&k) {
return G1_IDENTITY;
} else if is_one(&k) {
return *p;
} else if is_two(&k) {
return dbl_bn254(
p,
#[cfg(feature = "hints")]
hints,
);
}
let (max_limb, max_bit) = fcall_msb_pos_256(
&k,
#[cfg(feature = "hints")]
hints,
);
assert!(max_limb < 4 && max_bit < 64, "msb_pos hint out of range");
let max_limb = max_limb as usize;
let max_bit = max_bit as usize;
assert_eq!((k[max_limb] >> max_bit) & 1, 1, "The most significant bit of the scalar must be 1");
let x1: [u64; 4] = p[0..4].try_into().unwrap();
let y1: [u64; 4] = p[4..8].try_into().unwrap();
let mut q = SyscallPoint256 { x: x1, y: y1 };
let mut k_rec = [0u64; 4];
k_rec[max_limb] |= 1 << max_bit;
let mut limb = max_limb;
let mut bit = if max_bit == 0 {
limb -= 1;
63
} else {
max_bit - 1
};
let p = SyscallPoint256 { x: x1, y: y1 };
for i in (0..=limb).rev() {
for j in (0..=bit).rev() {
syscall_bn254_curve_dbl(
&mut q,
#[cfg(feature = "hints")]
hints,
);
if ((k[i] >> j) & 1) == 1 {
let mut params = SyscallBn254CurveAddParams { p1: &mut q, p2: &p };
syscall_bn254_curve_add(
&mut params,
#[cfg(feature = "hints")]
hints,
);
k_rec[i] |= 1 << j;
}
}
bit = 63;
}
assert!(eq(&k, &k_rec), "Reconstructed scalar does not match input scalar");
let x3 = q.x;
let y3 = q.y;
[x3[0], x3[1], x3[2], x3[3], y3[0], y3[1], y3[2], y3[3]]
}
pub fn scalar_mul_complete_safe_bn254(
p: &[u64; 8],
k: &[u64; 4],
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> Result<[u64; 8], u8> {
if eq(p, &G1_IDENTITY) {
return Ok(G1_IDENTITY);
}
let x: [u64; 4] = p[0..4].try_into().unwrap();
let y: [u64; 4] = p[4..8].try_into().unwrap();
if !lt(&x, &P) || !lt(&y, &P) {
return Err(G1_MUL_ERR_NOT_IN_FIELD);
}
if !is_on_curve_bn254(
p,
#[cfg(feature = "hints")]
hints,
) {
return Err(G1_MUL_ERR_NOT_ON_CURVE);
}
let k = reduce_fr_bn254(
k,
#[cfg(feature = "hints")]
hints,
);
Ok(scalar_mul_bn254(
p,
&k,
#[cfg(feature = "hints")]
hints,
))
}
#[cfg_attr(not(feature = "hints"), no_mangle)]
#[cfg_attr(feature = "hints", export_name = "hints_jacobian_to_affine_bn254_c")]
pub unsafe extern "C" fn jacobian_to_affine_bn254_c(
p_ptr: *const u64,
result_ptr: *mut u64,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> u8 {
let p = &*(p_ptr as *const [u64; 12]);
let result = &mut *(result_ptr as *mut [u64; 8]);
match jacobian_to_affine_bn254(
p,
#[cfg(feature = "hints")]
hints,
) {
G1_IDENTITY => {
*result = G1_IDENTITY;
1
}
affine => {
*result = affine;
0
}
}
}
#[cfg_attr(not(feature = "hints"), no_mangle)]
#[cfg_attr(feature = "hints", export_name = "hints_is_on_curve_bn254_c")]
pub unsafe extern "C" fn is_on_curve_bn254_c(
p_ptr: *const u64,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> u8 {
let p = &*(p_ptr as *const [u64; 8]);
is_on_curve_bn254(
p,
#[cfg(feature = "hints")]
hints,
) as u8
}
#[cfg_attr(not(feature = "hints"), no_mangle)]
#[cfg_attr(feature = "hints", export_name = "hints_add_bn254_c")]
pub unsafe extern "C" fn add_bn254_c(
p1_ptr: *const u64,
p2_ptr: *const u64,
result_ptr: *mut u64,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> u8 {
let p1 = &*(p1_ptr as *const [u64; 8]);
let p2 = &*(p2_ptr as *const [u64; 8]);
let result = &mut *(result_ptr as *mut [u64; 8]);
*result = add_bn254(
p1,
p2,
#[cfg(feature = "hints")]
hints,
);
if eq(result, &G1_IDENTITY) {
G1_ADD_SUCCESS_INFINITY
} else {
G1_ADD_SUCCESS
}
}
#[allow(dead_code)]
#[inline]
pub(crate) unsafe fn add_safe_bn254_c(
p1: *const u8,
p2: *const u8,
ret: *mut u8,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> u8 {
let p1_bytes: &[u8; 64] = &*(p1 as *const [u8; 64]);
let p2_bytes: &[u8; 64] = &*(p2 as *const [u8; 64]);
let ret_bytes: &mut [u8; 64] = &mut *(ret as *mut [u8; 64]);
let p1_u64 = g1_bytes_be_to_u64_le_bn254(p1_bytes);
let p2_u64 = g1_bytes_be_to_u64_le_bn254(p2_bytes);
let result = match add_complete_safe_bn254(
&p1_u64,
&p2_u64,
#[cfg(feature = "hints")]
hints,
) {
Ok(r) => r,
Err(code) => return code,
};
g1_u64_le_to_bytes_be_bn254(&result, ret_bytes);
if eq(&result, &G1_IDENTITY) {
G1_ADD_SUCCESS_INFINITY
} else {
G1_ADD_SUCCESS
}
}
#[cfg_attr(not(feature = "hints"), no_mangle)]
#[cfg_attr(feature = "hints", export_name = "hints_scalar_mul_bn254_c")]
pub unsafe extern "C" fn scalar_mul_bn254_c(
p_ptr: *const u64,
k_ptr: *const u64,
result_ptr: *mut u64,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> u8 {
let p = &*(p_ptr as *const [u64; 8]);
let k = &*(k_ptr as *const [u64; 4]);
let result = &mut *(result_ptr as *mut [u64; 8]);
match scalar_mul_bn254(
p,
k,
#[cfg(feature = "hints")]
hints,
) {
G1_IDENTITY => {
*result = G1_IDENTITY;
1
}
product => {
*result = product;
0
}
}
}
#[allow(dead_code)]
#[inline]
pub(crate) unsafe fn scalar_mul_safe_bn254_c(
point: *const u8,
scalar: *const u8,
ret: *mut u8,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> u8 {
let point_bytes: &[u8; 64] = &*(point as *const [u8; 64]);
let scalar_bytes: &[u8; 32] = &*(scalar as *const [u8; 32]);
let ret_bytes: &mut [u8; 64] = &mut *(ret as *mut [u8; 64]);
let point_u64 = g1_bytes_be_to_u64_le_bn254(point_bytes);
let scalar_u64 = scalar_bytes_be_to_u64_le_bn254(scalar_bytes);
let product = match scalar_mul_complete_safe_bn254(
&point_u64,
&scalar_u64,
#[cfg(feature = "hints")]
hints,
) {
Ok(r) => r,
Err(code) => return code,
};
g1_u64_le_to_bytes_be_bn254(&product, ret_bytes);
if product == G1_IDENTITY {
G1_MUL_SUCCESS_INFINITY
} else {
G1_MUL_SUCCESS
}
}
pub fn g1_bytes_be_to_u64_le_bn254(bytes: &[u8; 64]) -> [u64; 8] {
let mut result = [0u64; 8];
for i in 0..4 {
for j in 0..8 {
result[3 - i] |= (bytes[i * 8 + j] as u64) << (8 * (7 - j));
}
}
for i in 0..4 {
for j in 0..8 {
result[7 - i] |= (bytes[32 + i * 8 + j] as u64) << (8 * (7 - j));
}
}
result
}
#[allow(dead_code)]
fn g1_u64_le_to_bytes_be_bn254(limbs: &[u64; 8], bytes: &mut [u8; 64]) {
for i in 0..4 {
let limb = limbs[3 - i];
for j in 0..8 {
bytes[i * 8 + j] = ((limb >> (8 * (7 - j))) & 0xff) as u8;
}
}
for i in 0..4 {
let limb = limbs[7 - i];
for j in 0..8 {
bytes[32 + i * 8 + j] = ((limb >> (8 * (7 - j))) & 0xff) as u8;
}
}
}
pub fn scalar_bytes_be_to_u64_le_bn254(bytes: &[u8; 32]) -> [u64; 4] {
let mut result = [0u64; 4];
for i in 0..4 {
for j in 0..8 {
result[3 - i] |= (bytes[i * 8 + j] as u64) << (8 * (7 - j));
}
}
result
}