use soroban_sdk::crypto::bn254::{Bn254G1Affine, Bn254G2Affine, Fr};
use soroban_sdk::{Env, Vec};
use super::error::ZKError;
use super::types::{G1Point, G2Point, Scalar};
pub fn bn254_g1_add(env: &Env, p1: &G1Point, p2: &G1Point) -> Result<G1Point, ZKError> {
let a = Bn254G1Affine::from_bytes(p1.bytes.clone());
let b = Bn254G1Affine::from_bytes(p2.bytes.clone());
let result = env.crypto().bn254().g1_add(&a, &b);
Ok(G1Point {
bytes: result.to_bytes(),
})
}
pub fn bn254_g1_mul(env: &Env, point: &G1Point, scalar: &Scalar) -> Result<G1Point, ZKError> {
let p = Bn254G1Affine::from_bytes(point.bytes.clone());
let s = Fr::from_bytes(scalar.bytes.clone());
let result = env.crypto().bn254().g1_mul(&p, &s);
Ok(G1Point {
bytes: result.to_bytes(),
})
}
pub fn bn254_pairing_check(
env: &Env,
g1_points: &[G1Point],
g2_points: &[G2Point],
) -> Result<bool, ZKError> {
if g1_points.len() != g2_points.len() {
return Err(ZKError::InvalidInput);
}
if g1_points.is_empty() {
return Err(ZKError::InvalidInput);
}
let mut vp1: Vec<Bn254G1Affine> = Vec::new(env);
let mut vp2: Vec<Bn254G2Affine> = Vec::new(env);
for p in g1_points {
vp1.push_back(Bn254G1Affine::from_bytes(p.bytes.clone()));
}
for p in g2_points {
vp2.push_back(Bn254G2Affine::from_bytes(p.bytes.clone()));
}
Ok(env.crypto().bn254().pairing_check(vp1, vp2))
}
#[cfg(feature = "hazmat-crypto")]
#[allow(clippy::too_many_arguments)]
pub fn poseidon_permutation(
env: &Env,
input: &soroban_sdk::Vec<soroban_sdk::U256>,
field: soroban_sdk::Symbol,
t: u32,
d: u32,
rounds_f: u32,
rounds_p: u32,
mds: &soroban_sdk::Vec<soroban_sdk::Vec<soroban_sdk::U256>>,
round_constants: &soroban_sdk::Vec<soroban_sdk::Vec<soroban_sdk::U256>>,
) -> soroban_sdk::Vec<soroban_sdk::U256> {
let hazmat = env.crypto_hazmat();
hazmat.poseidon_permutation(input, field, t, d, rounds_f, rounds_p, mds, round_constants)
}
#[cfg(feature = "hazmat-crypto")]
#[allow(clippy::too_many_arguments)]
pub fn poseidon2_permutation(
env: &Env,
input: &soroban_sdk::Vec<soroban_sdk::U256>,
field: soroban_sdk::Symbol,
t: u32,
d: u32,
rounds_f: u32,
rounds_p: u32,
mat_internal_diag_m_1: &soroban_sdk::Vec<soroban_sdk::U256>,
round_constants: &soroban_sdk::Vec<soroban_sdk::Vec<soroban_sdk::U256>>,
) -> soroban_sdk::Vec<soroban_sdk::U256> {
let hazmat = env.crypto_hazmat();
hazmat.poseidon2_permutation(
input,
field,
t,
d,
rounds_f,
rounds_p,
mat_internal_diag_m_1,
round_constants,
)
}
#[cfg(feature = "hazmat-crypto")]
pub struct Poseidon2Params {
pub field: soroban_sdk::Symbol,
pub t: u32,
pub d: u32,
pub rounds_f: u32,
pub rounds_p: u32,
pub mat_internal_diag_m_1: soroban_sdk::Vec<soroban_sdk::U256>,
pub round_constants: soroban_sdk::Vec<soroban_sdk::Vec<soroban_sdk::U256>>,
}
#[cfg(feature = "hazmat-crypto")]
pub fn poseidon2_hash(
env: &Env,
params: &Poseidon2Params,
a: &soroban_sdk::U256,
b: &soroban_sdk::U256,
) -> soroban_sdk::U256 {
let mut input = soroban_sdk::Vec::new(env);
input.push_back(a.clone());
input.push_back(b.clone());
let zero = soroban_sdk::U256::from_u32(env, 0);
for _ in 2..params.t {
input.push_back(zero.clone());
}
let output = poseidon2_permutation(
env,
&input,
params.field.clone(),
params.t,
params.d,
params.rounds_f,
params.rounds_p,
¶ms.mat_internal_diag_m_1,
¶ms.round_constants,
);
output.get(0).unwrap()
}
#[cfg(feature = "hazmat-crypto")]
pub fn poseidon2_hash_single(
env: &Env,
params: &Poseidon2Params,
input: &soroban_sdk::U256,
) -> soroban_sdk::U256 {
let zero = soroban_sdk::U256::from_u32(env, 0);
poseidon2_hash(env, params, input, &zero)
}
#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::{BytesN, Env};
#[test]
fn test_pairing_check_empty_input_fails() {
let result = bn254_pairing_check(&Env::default(), &[], &[]);
assert_eq!(result, Err(ZKError::InvalidInput));
}
#[test]
fn test_pairing_check_mismatched_lengths() {
let env = Env::default();
let g1 = G1Point {
bytes: BytesN::from_array(&env, &[0u8; 64]),
};
let result = bn254_pairing_check(&env, &[g1], &[]);
assert_eq!(result, Err(ZKError::InvalidInput));
}
}