diff --git a/scripts/setup/solana.dic b/scripts/setup/solana.dic index f7ba5508..a8940bb5 100644 --- a/scripts/setup/solana.dic +++ b/scripts/setup/solana.dic @@ -70,3 +70,6 @@ mainnet getters PRNG middleware +BN254 +G1 +G2 diff --git a/sdk/pinocchio/src/curves/bn254/addition.rs b/sdk/pinocchio/src/curves/bn254/addition.rs new file mode 100644 index 00000000..140e2bbe --- /dev/null +++ b/sdk/pinocchio/src/curves/bn254/addition.rs @@ -0,0 +1,31 @@ +//! Addition operations on the BN254 curve. + +use super::{alt_bn128_group_op, ALT_BN128_G1_POINT_SIZE}; +use crate::program_error::ProgramError; + +/// Input size for the add operation. +pub const ALT_BN128_ADDITION_INPUT_SIZE: usize = ALT_BN128_G1_POINT_SIZE * 2; // 128 + +/// Output size for the add operation. +pub const ALT_BN128_ADDITION_OUTPUT_SIZE: usize = ALT_BN128_G1_POINT_SIZE; // 64 + +const ALT_BN128_G1_ADD_BE: u64 = 0; +#[allow(dead_code)] +const ALT_BN128_G1_SUB_BE: u64 = 1; // not implemented in the syscall + +/// Add two G1 points on the BN254 curve in big-endian (EIP-197) encoding. +/// +/// # Arguments +/// +/// * `input` - Two consecutive G1 points in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing the result of the addition in big-endian (EIP-197) encoding, +/// or an error if the input is invalid. +#[inline(always)] +pub fn alt_bn128_g1_addition_be( + input: &[u8; ALT_BN128_ADDITION_INPUT_SIZE], +) -> Result<[u8; ALT_BN128_ADDITION_OUTPUT_SIZE], ProgramError> { + alt_bn128_group_op(input, ALT_BN128_G1_ADD_BE) +} diff --git a/sdk/pinocchio/src/curves/bn254/compression.rs b/sdk/pinocchio/src/curves/bn254/compression.rs new file mode 100644 index 00000000..28e832d8 --- /dev/null +++ b/sdk/pinocchio/src/curves/bn254/compression.rs @@ -0,0 +1,118 @@ +//! Compression/decompression of points on the BN254 curve. + +use super::{ALT_BN128_G1_POINT_SIZE, ALT_BN128_G2_POINT_SIZE}; +use crate::program_error::ProgramError; + +#[cfg(target_os = "solana")] +use crate::syscalls::sol_alt_bn128_compression; + +// compression sizes +pub const ALT_BN128_G1_COMPRESSED_POINT_SIZE: usize = ALT_BN128_G1_POINT_SIZE / 2; // 32 +pub const ALT_BN128_G2_COMPRESSED_POINT_SIZE: usize = ALT_BN128_G2_POINT_SIZE / 2; // 64 + +// compression operations +const ALT_BN128_G1_COMPRESS_BE: u64 = 0; +const ALT_BN128_G1_DECOMPRESS_BE: u64 = 1; +const ALT_BN128_G2_COMPRESS_BE: u64 = 2; +const ALT_BN128_G2_DECOMPRESS_BE: u64 = 3; + +/// Compress a G1 point on the BN254 curve. +/// +/// # Arguments +/// +/// * `input` - A G1 point in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing the compressed G1 point in big-endian (EIP-197) encoding, +/// or an error if the input is not a valid G1 point. +#[inline(always)] +pub fn alt_bn128_g1_compress_be( + input: &[u8; ALT_BN128_G1_POINT_SIZE], +) -> Result<[u8; ALT_BN128_G1_COMPRESSED_POINT_SIZE], ProgramError> { + alt_bn128_compression(input, ALT_BN128_G1_COMPRESS_BE) +} + +/// Decompress a G1 point on the BN254 curve. +/// +/// # Arguments +/// +/// * `input` - A compressed G1 point in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing the decompressed G1 point in big-endian (EIP-197) encoding, +/// or an error if the input is not a valid compressed G1 point. +#[inline(always)] +pub fn alt_bn128_g1_decompress_be( + input: &[u8; ALT_BN128_G1_COMPRESSED_POINT_SIZE], +) -> Result<[u8; ALT_BN128_G1_POINT_SIZE], ProgramError> { + alt_bn128_compression(input, ALT_BN128_G1_DECOMPRESS_BE) +} + +/// Compress a G2 point on the BN254 curve. +/// +/// # Arguments +/// +/// * `input` - A G2 point in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing the compressed G2 point in big-endian (EIP-197) encoding, +/// or an error if the input is not a valid G2 point. +#[inline(always)] +pub fn alt_bn128_g2_compress_be( + input: &[u8; ALT_BN128_G2_POINT_SIZE], +) -> Result<[u8; ALT_BN128_G2_COMPRESSED_POINT_SIZE], ProgramError> { + alt_bn128_compression(input, ALT_BN128_G2_COMPRESS_BE) +} + +/// Decompress a G2 point on the BN254 curve. +/// +/// # Arguments +/// +/// * `input` - A compressed G2 point in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing the decompressed G2 point in big-endian (EIP-197) encoding, +/// or an error if the input is not a valid compressed G2 point. +#[inline(always)] +pub fn alt_bn128_g2_decompress_be( + input: &[u8; ALT_BN128_G2_COMPRESSED_POINT_SIZE], +) -> Result<[u8; ALT_BN128_G2_POINT_SIZE], ProgramError> { + alt_bn128_compression(input, ALT_BN128_G2_DECOMPRESS_BE) +} + +#[inline] +fn alt_bn128_compression( + input: &[u8], + op: u64, +) -> Result<[u8; OUTPUT_DATA_SIZE], ProgramError> { + // Call via a system call to perform the calculation + #[cfg(target_os = "solana")] + { + let mut bytes = core::mem::MaybeUninit::<[u8; OUTPUT_DATA_SIZE]>::uninit(); + + let result = unsafe { + sol_alt_bn128_compression( + op, + input as *const _ as *const u8, + input.len() as u64, + bytes.as_mut_ptr() as *mut u8, + ) + }; + + match result { + // SAFETY: The syscall has initialized the bytes. + crate::SUCCESS => Ok(unsafe { bytes.assume_init() }), + _ => Err(ProgramError::InvalidArgument), + } + } + + #[cfg(not(target_os = "solana"))] + { + core::hint::black_box((input, op)); + panic!("alt_bn128_compression is only available on target `solana`") + } +} diff --git a/sdk/pinocchio/src/curves/bn254/mod.rs b/sdk/pinocchio/src/curves/bn254/mod.rs new file mode 100644 index 00000000..68d9bd78 --- /dev/null +++ b/sdk/pinocchio/src/curves/bn254/mod.rs @@ -0,0 +1,56 @@ +//! BN254 curve operations + +pub mod addition; +pub mod compression; +pub mod multiplication; +pub mod pairing; + +pub use addition::*; +pub use compression::*; +pub use multiplication::*; +pub use pairing::*; + +use crate::program_error::ProgramError; + +#[cfg(target_os = "solana")] +use crate::syscalls::sol_alt_bn128_group_op; + +/// Size of the EC point field, in bytes. +pub const ALT_BN128_FIELD_SIZE: usize = 32; +/// A group element in G1 consists of two field elements `(x, y)`. +pub const ALT_BN128_G1_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 2; +/// Elements in G2 is represented by 2 field-extension elements `(x, y)`. +pub const ALT_BN128_G2_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 4; + +#[inline] +fn alt_bn128_group_op( + input: &[u8], + op: u64, +) -> Result<[u8; OUTPUT_DATA_SIZE], ProgramError> { + // Call via a system call to perform the calculation + #[cfg(target_os = "solana")] + { + let mut bytes = core::mem::MaybeUninit::<[u8; OUTPUT_DATA_SIZE]>::uninit(); + + let result = unsafe { + sol_alt_bn128_group_op( + op, + input as *const _ as *const u8, + input.len() as u64, + bytes.as_mut_ptr() as *mut u8, + ) + }; + + match result { + // SAFETY: The syscall has initialized the bytes. + crate::SUCCESS => Ok(unsafe { bytes.assume_init() }), + _ => Err(ProgramError::InvalidArgument), + } + } + + #[cfg(not(target_os = "solana"))] + { + core::hint::black_box((input, op)); + panic!("alt_bn128_group_op is only available on target `solana`") + } +} diff --git a/sdk/pinocchio/src/curves/bn254/multiplication.rs b/sdk/pinocchio/src/curves/bn254/multiplication.rs new file mode 100644 index 00000000..92274a84 --- /dev/null +++ b/sdk/pinocchio/src/curves/bn254/multiplication.rs @@ -0,0 +1,31 @@ +//! Multiplication operations on the BN254 curve. + +use super::{alt_bn128_group_op, ALT_BN128_FIELD_SIZE, ALT_BN128_G1_POINT_SIZE}; +use crate::program_error::ProgramError; + +/// Input size for the multiplication operation. +pub const ALT_BN128_MULTIPLICATION_INPUT_SIZE: usize = + ALT_BN128_G1_POINT_SIZE + ALT_BN128_FIELD_SIZE; // 96 + +/// Output size for the multiplication operation. +pub const ALT_BN128_MULTIPLICATION_OUTPUT_SIZE: usize = ALT_BN128_G1_POINT_SIZE; // 64 + +const ALT_BN128_G1_MUL_BE: u64 = 2; + +/// Multiply a G1 point by a scalar on the BN254 curve in big-endian (EIP-197) encoding. +/// +/// # Arguments +/// +/// * `input` - A G1 point in big-endian (EIP-197) encoding, +/// followed by a scalar in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing the result of the multiplication in big-endian (EIP-197) +/// encoding, or an error if the input is invalid. +#[inline(always)] +pub fn alt_bn128_g1_multiplication_be( + input: &[u8; ALT_BN128_MULTIPLICATION_INPUT_SIZE], +) -> Result<[u8; ALT_BN128_MULTIPLICATION_OUTPUT_SIZE], ProgramError> { + alt_bn128_group_op(input, ALT_BN128_G1_MUL_BE) +} diff --git a/sdk/pinocchio/src/curves/bn254/pairing.rs b/sdk/pinocchio/src/curves/bn254/pairing.rs new file mode 100644 index 00000000..088c6e2d --- /dev/null +++ b/sdk/pinocchio/src/curves/bn254/pairing.rs @@ -0,0 +1,33 @@ +//! Pairing operations on the BN254 curve. + +use super::{alt_bn128_group_op, ALT_BN128_G1_POINT_SIZE, ALT_BN128_G2_POINT_SIZE}; +use crate::program_error::ProgramError; + +/// Pair element size. +pub const ALT_BN128_PAIRING_ELEMENT_SIZE: usize = ALT_BN128_G1_POINT_SIZE + ALT_BN128_G2_POINT_SIZE; // 192 + +const ALT_BN128_PAIRING_BE: u64 = 3; + +/// Checks whether the product of pairings of a sequence of G1 and G2 points (in big-endian EIP-197 encoding) +/// on the BN254 curve evaluates to the identity element (1). +/// +/// # Arguments +/// +/// * `input` - A sequence of pairs of G1 and G2 points in big-endian (EIP-197) encoding. +/// +/// # Returns +/// +/// A `Result` containing: +/// - `Ok(true)` if the pairing product equals 1, +/// - `Ok(false)` otherwise, +/// - `Err(ProgramError)` if the input is invalid. +/// +/// Note: This function does **not** check if the input has the correct length. +/// Currently, if the length is invalid, it will not return an error; instead it will use only +/// multiples of [`ALT_BN128_PAIRING_ELEMENT_SIZE`] bytes and discard the rest. +/// After SIMD-0334 is implemented, it will return an error if the length is invalid, +/// incurring the cost of the syscall. +#[inline(always)] +pub fn alt_bn128_is_pairing_valid_be(input: &[u8]) -> Result { + alt_bn128_group_op::<32>(input, ALT_BN128_PAIRING_BE).map(|data| data[31] == 1) +} diff --git a/sdk/pinocchio/src/curves/mod.rs b/sdk/pinocchio/src/curves/mod.rs new file mode 100644 index 00000000..08ea7879 --- /dev/null +++ b/sdk/pinocchio/src/curves/mod.rs @@ -0,0 +1,3 @@ +//! Functions for working with elliptic curves. + +pub mod bn254; diff --git a/sdk/pinocchio/src/lib.rs b/sdk/pinocchio/src/lib.rs index b4ae9098..de54cc9b 100644 --- a/sdk/pinocchio/src/lib.rs +++ b/sdk/pinocchio/src/lib.rs @@ -225,6 +225,7 @@ extern crate std; pub mod account_info; pub mod cpi; +pub mod curves; pub mod entrypoint; pub mod instruction; pub mod log;