anza-cryptography-source/syscall/bn254-syscall/src/addition.rs
Sam Kim ed4a212bcb
[solana-bn254-syscall] Add solana-bn254-syscall crate (#13)
* add `solana-bn254-syscall` crate

* use `Validate::Yes` for `G1::deserialize_with_mode`

* fix crate name

* inherit dependencies from workspace

* update edition to 2021

* remove unnecessary `is_on_curve` check

* add `#[inline(always)]`

* make `PodG1` and `PodG2` pub(crate)

* remove unnecessary `is_on_curve` check

* remove custom logic for legacy versions

* `convert_endianness` -> `swap_endianness`

* simplify `swap_endianness`

* update function return types to arrays instead of vecs

* copy over docs

* update cargo lock

* copy over unit tests for serialization

* clean up docs for the syscall implementation functions

* Apply suggestions from code review

Co-authored-by: Stanislav Ladyzhenskiy <LStan@users.noreply.github.com>

* Update syscall/bn254-syscall/src/multiplication.rs

Co-authored-by: Stanislav Ladyzhenskiy <LStan@users.noreply.github.com>

* cargo lock

* remove `all-features` and `rustdoc-args`

* Apply suggestions from code review

Co-authored-by: Stanislav Ladyzhenskiy <LStan@users.noreply.github.com>

* Update syscall/bn254-syscall/Cargo.toml

Co-authored-by: Stanislav Ladyzhenskiy <LStan@users.noreply.github.com>

* remove `include = ...`

---------

Co-authored-by: Stanislav Ladyzhenskiy <LStan@users.noreply.github.com>
2026-05-19 08:56:06 +09:00

143 lines
4.8 KiB
Rust

use {
crate::{
swap_endianness, Endianness, PodG1, PodG2, ALT_BN128_FIELD_SIZE, ALT_BN128_FQ2_SIZE,
ALT_BN128_G1_POINT_SIZE, ALT_BN128_G2_POINT_SIZE, G1, G2,
},
ark_serialize::{CanonicalSerialize, Compress},
};
/// Input size for the g1 add operation.
pub const ALT_BN128_G1_ADDITION_INPUT_SIZE: usize = ALT_BN128_G1_POINT_SIZE * 2; // 128
/// Input size for the g2 add operation.
pub const ALT_BN128_G2_ADDITION_INPUT_SIZE: usize = ALT_BN128_G2_POINT_SIZE * 2; // 256
/// The enum is used to version changes to the `alt_bn128_versioned_g1_addition` function.
pub enum VersionedG1Addition {
V0,
}
/// The enum is used to version changes to the `alt_bn128_versioned_g2_addition` function.
pub enum VersionedG2Addition {
V0,
}
/// The implementation of the `sol_alt_bn128_group_op` syscall G1 addition operation
/// (group operation index 0x00 for BE input/output, 0x80 for LE input/output).
///
/// **Security Note**
///
/// Because the BN254 G1 group has a cofactor of 1, the subgroup check is equivalent
/// to verifying the point is on the curve. This function fully validates the input point.
///
/// **Warning**
///
/// This is consensus-critical Agave validator code. Modifying this
/// function can result in a network fork. See the [crate-level documentation](crate)
/// for strict guidelines on SIMD approvals and versioning.
pub fn alt_bn128_versioned_g1_addition(
_version: VersionedG1Addition,
input: &[u8],
endianness: Endianness,
) -> Option<[u8; ALT_BN128_G1_POINT_SIZE]> {
let is_valid_len = match endianness {
Endianness::BE => input.len() <= ALT_BN128_G1_ADDITION_INPUT_SIZE,
Endianness::LE => input.len() == ALT_BN128_G1_ADDITION_INPUT_SIZE,
};
if !is_valid_len {
return None;
}
let mut padded_input = [0u8; ALT_BN128_G1_ADDITION_INPUT_SIZE];
padded_input[..input.len()].copy_from_slice(input);
let (p_bytes, q_bytes) = padded_input.split_at(ALT_BN128_G1_POINT_SIZE);
let (p, q) = match endianness {
Endianness::BE => (
PodG1::from_be_bytes(p_bytes)?.into_affine()?,
PodG1::from_be_bytes(q_bytes)?.into_affine()?,
),
Endianness::LE => (
PodG1::from_le_bytes(p_bytes)?.into_affine()?,
PodG1::from_le_bytes(q_bytes)?.into_affine()?,
),
};
let result_point_affine: G1 = (p + q).into();
let mut result_point_data = [0u8; ALT_BN128_G1_POINT_SIZE];
result_point_affine
.x
.serialize_with_mode(&mut result_point_data[..ALT_BN128_FIELD_SIZE], Compress::No)
.ok()?;
result_point_affine
.y
.serialize_with_mode(&mut result_point_data[ALT_BN128_FIELD_SIZE..], Compress::No)
.ok()?;
match endianness {
Endianness::BE => Some(swap_endianness::<
ALT_BN128_FIELD_SIZE,
ALT_BN128_G1_POINT_SIZE,
>(result_point_data)),
Endianness::LE => Some(result_point_data),
}
}
/// The implementation of the `sol_alt_bn128_group_op` syscall G2 addition operation
/// (group operation index 0x04 for BE input/output, 0x84 for LE input/output).
///
/// **Security Note**
///
/// Unlike G1, which has a cofactor of 1, the group G2 has a high cofactor.
/// This G2 addition function validates only the curve equation; it does not perform
/// a subgroup (coset) check.
///
/// **Warning**
///
/// This is consensus-critical Agave validator code. Modifying this function can
/// result in a network fork. See the [crate-level documentation](crate) for strict
/// guidelines on SIMD approvals and versioning.
pub fn alt_bn128_versioned_g2_addition(
_version: VersionedG2Addition,
input: &[u8],
endianness: Endianness,
) -> Option<[u8; ALT_BN128_G2_POINT_SIZE]> {
if input.len() != ALT_BN128_G2_ADDITION_INPUT_SIZE {
return None;
}
let (p_bytes, q_bytes) = input.split_at(ALT_BN128_G2_POINT_SIZE);
let (p, q) = match endianness {
Endianness::BE => (
PodG2::from_be_bytes(p_bytes)?.into_affine_unchecked()?,
PodG2::from_be_bytes(q_bytes)?.into_affine_unchecked()?,
),
Endianness::LE => (
PodG2::from_le_bytes(p_bytes)?.into_affine_unchecked()?,
PodG2::from_le_bytes(q_bytes)?.into_affine_unchecked()?,
),
};
let result_point_affine: G2 = (p + q).into();
let mut result_point_data = [0u8; ALT_BN128_G2_POINT_SIZE];
result_point_affine
.x
.serialize_with_mode(&mut result_point_data[..ALT_BN128_FQ2_SIZE], Compress::No)
.ok()?;
result_point_affine
.y
.serialize_with_mode(&mut result_point_data[ALT_BN128_FQ2_SIZE..], Compress::No)
.ok()?;
match endianness {
Endianness::BE => {
Some(swap_endianness::<ALT_BN128_FQ2_SIZE, ALT_BN128_G2_POINT_SIZE>(result_point_data))
}
Endianness::LE => Some(result_point_data),
}
}