Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions scripts/setup/solana.dic
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,6 @@ mainnet
getters
PRNG
middleware
BN254
G1
G2
214 changes: 214 additions & 0 deletions sdk/pinocchio/src/curves/bn254/compression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
//! Compression/decompression of points on the BN254 curve.

use super::{ALT_BN128_G1_SIZE, ALT_BN128_G2_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_SIZE: usize = ALT_BN128_G1_SIZE / 2; // 32
pub const ALT_BN128_G2_COMPRESSED_SIZE: usize = ALT_BN128_G2_SIZE / 2; // 64

// compression operations
const ALT_BN128_G1_COMPRESS: u64 = 0;
const ALT_BN128_G1_DECOMPRESS: u64 = 1;
const ALT_BN128_G2_COMPRESS: u64 = 2;
const ALT_BN128_G2_DECOMPRESS: 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.
///
/// Note: This function does **not** check if the input has the correct length.
/// It will return an error if the length is invalid, incurring the cost of the syscall.
#[inline(always)]
pub fn alt_bn128_g1_compress(
input: &[u8],
) -> Result<[u8; ALT_BN128_G1_COMPRESSED_SIZE], ProgramError> {
alt_bn128_compression(input, ALT_BN128_G1_COMPRESS)
}

/// 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.
///
/// Note: This function checks if the input has the correct length,
/// returning an error without incurring the cost of the syscall.
pub fn checked_alt_bn128_g1_compress(
input: &[u8],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of taking in a &[u8], consider using a &[[u8;ALT_BN128_G1_SIZE]]. This will avoid the need for the length check at runtime for properly defined constants.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will be the input type of alt_bn128_compression?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, will it be convenient from the user's perspective if they have just &[u8] input?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why &[[u8;ALT_BN128_G1_SIZE]] if there is only one point in the input? Maybe &[u8;ALT_BN128_G1_SIZE]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, just do the one, you are right. For multiple we want the array, for single, we want just the one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I can change input type to &[u8;SIZE] for all compressions, then checked_ variants will be unnecessary. But I'm still unsure about the convenience of usage for callers.

) -> Result<[u8; ALT_BN128_G1_COMPRESSED_SIZE], ProgramError> {
if input.len() != ALT_BN128_G1_SIZE {
return Err(ProgramError::InvalidArgument);
}
alt_bn128_compression(input, ALT_BN128_G1_COMPRESS)
}

/// 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.
///
/// Note: This function does **not** check if the input has the correct length.
/// It will return an error if the length is invalid, incurring the cost of the syscall.
#[inline(always)]
pub fn alt_bn128_g1_decompress(input: &[u8]) -> Result<[u8; ALT_BN128_G1_SIZE], ProgramError> {
alt_bn128_compression(input, ALT_BN128_G1_DECOMPRESS)
}

/// 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.
///
/// Note: This function checks if the input has the correct length,
/// returning an error without incurring the cost of the syscall.
pub fn checked_alt_bn128_g1_decompress(
input: &[u8],
) -> Result<[u8; ALT_BN128_G1_SIZE], ProgramError> {
if input.len() != ALT_BN128_G1_COMPRESSED_SIZE {
return Err(ProgramError::InvalidArgument);
}
alt_bn128_compression(input, ALT_BN128_G1_DECOMPRESS)
}

/// 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.
///
/// Note: This function does **not** check if the input has the correct length.
/// It will return an error if the length is invalid, incurring the cost of the syscall.
#[inline(always)]
pub fn alt_bn128_g2_compress(
input: &[u8],
) -> Result<[u8; ALT_BN128_G2_COMPRESSED_SIZE], ProgramError> {
alt_bn128_compression(input, ALT_BN128_G2_COMPRESS)
}

/// 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.
///
/// Note: This function checks if the input has the correct length,
/// returning an error without incurring the cost of the syscall.
pub fn checked_alt_bn128_g2_compress(
input: &[u8],
) -> Result<[u8; ALT_BN128_G2_COMPRESSED_SIZE], ProgramError> {
if input.len() != ALT_BN128_G2_SIZE {
return Err(ProgramError::InvalidArgument);
}
alt_bn128_compression(input, ALT_BN128_G2_COMPRESS)
}

/// 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.
///
/// Note: This function does **not** check if the input has the correct length.
/// It will return an error if the length is invalid, incurring the cost of the syscall.
#[inline(always)]
pub fn alt_bn128_g2_decompress(input: &[u8]) -> Result<[u8; ALT_BN128_G2_SIZE], ProgramError> {
alt_bn128_compression(input, ALT_BN128_G2_DECOMPRESS)
}

/// 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.
///
/// Note: This function checks if the input has the correct length,
/// returning an error without incurring the cost of the syscall.
pub fn checked_alt_bn128_g2_decompress(
input: &[u8],
) -> Result<[u8; ALT_BN128_G2_SIZE], ProgramError> {
if input.len() != ALT_BN128_G2_COMPRESSED_SIZE {
return Err(ProgramError::InvalidArgument);
}
alt_bn128_compression(input, ALT_BN128_G2_DECOMPRESS)
}

#[inline]
fn alt_bn128_compression<const OUTPUT_DATA_LEN: usize>(
input: &[u8],
op: u64,
) -> Result<[u8; OUTPUT_DATA_LEN], ProgramError> {
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
{
let mut bytes = core::mem::MaybeUninit::<[u8; OUTPUT_DATA_LEN]>::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`")
}
}
Loading