Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,68 +1,7 @@
//! Blake2 precompile. More details in [`run`]

use crate::{
crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult,
};

const F_ROUND: u64 = 1;
const INPUT_LENGTH: usize = 213;

/// Blake2 precompile
pub const FUN: Precompile = Precompile::new(PrecompileId::Blake2F, crate::u64_to_address(9), run);

/// reference: <https://eips.ethereum.org/EIPS/eip-152>
/// input format:
/// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f]
pub fn run(input: &[u8], gas_limit: u64) -> PrecompileResult {
if input.len() != INPUT_LENGTH {
return Err(PrecompileError::Blake2WrongLength);
}

// Parse number of rounds (4 bytes)
let rounds = u32::from_be_bytes(input[..4].try_into().unwrap());
let gas_used = rounds as u64 * F_ROUND;
if gas_used > gas_limit {
return Err(PrecompileError::OutOfGas);
}

// Parse final block flag
let f = match input[212] {
0 => false,
1 => true,
_ => return Err(PrecompileError::Blake2WrongFinalIndicatorFlag),
};

// Parse state vector h (8 × u64)
let mut h = [0u64; 8];
input[4..68]
.chunks_exact(8)
.enumerate()
.for_each(|(i, chunk)| {
h[i] = u64::from_le_bytes(chunk.try_into().unwrap());
});

// Parse message block m (16 × u64)
let mut m = [0u64; 16];
input[68..196]
.chunks_exact(8)
.enumerate()
.for_each(|(i, chunk)| {
m[i] = u64::from_le_bytes(chunk.try_into().unwrap());
});

// Parse offset counters
let t_0 = u64::from_le_bytes(input[196..204].try_into().unwrap());
let t_1 = u64::from_le_bytes(input[204..212].try_into().unwrap());

crypto().blake2_compress(rounds, &mut h, &m, &[t_0, t_1], f);

let mut out = [0u8; 64];
for (i, h) in (0..64).step_by(8).zip(h.iter()) {
out[i..i + 8].copy_from_slice(&h.to_le_bytes());
}

Ok(PrecompileOutput::new(gas_used, out.into()))
}
//! Pure cryptographic implementation for BLAKE2b compression.
//!
//! This module contains the BLAKE2b compression function and its optimized
//! AVX2 variant, isolated from the precompile runner logic.

/// Blake2 algorithm
pub mod algo {
Expand Down Expand Up @@ -627,23 +566,3 @@ mod avx2 {
*c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(2, 1, 0, 3));
}
}

#[cfg(test)]
mod tests {
use super::*;
use primitives::hex;
use std::time::Instant;

#[test]
fn perfblake2() {
let input = [hex!("0000040048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b616162636465666768696a6b6c6d6e6f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")
,hex!("0000020048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")
,hex!("0000004048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")];

let time = Instant::now();
for i in 0..3000 {
let _ = run(&input[i % 3], u64::MAX).unwrap();
}
println!("duration: {:?}", time.elapsed());
}
}
91 changes: 91 additions & 0 deletions crates/precompile/src/blake2/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Blake2 precompile. More details in [`run`]

pub mod crypto;

/// Re-export for backward compatibility.
pub use crypto::algo;

use crate::{
crypto as crypto_provider, Precompile, PrecompileError, PrecompileId, PrecompileOutput,
PrecompileResult,
};

const F_ROUND: u64 = 1;
const INPUT_LENGTH: usize = 213;

/// Blake2 precompile
pub const FUN: Precompile = Precompile::new(PrecompileId::Blake2F, crate::u64_to_address(9), run);

/// reference: <https://eips.ethereum.org/EIPS/eip-152>
/// input format:
/// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f]
pub fn run(input: &[u8], gas_limit: u64) -> PrecompileResult {
if input.len() != INPUT_LENGTH {
return Err(PrecompileError::Blake2WrongLength);
}

// Parse number of rounds (4 bytes)
let rounds = u32::from_be_bytes(input[..4].try_into().unwrap());
let gas_used = rounds as u64 * F_ROUND;
if gas_used > gas_limit {
return Err(PrecompileError::OutOfGas);
}

// Parse final block flag
let f = match input[212] {
0 => false,
1 => true,
_ => return Err(PrecompileError::Blake2WrongFinalIndicatorFlag),
};

// Parse state vector h (8 × u64)
let mut h = [0u64; 8];
input[4..68]
.chunks_exact(8)
.enumerate()
.for_each(|(i, chunk)| {
h[i] = u64::from_le_bytes(chunk.try_into().unwrap());
});

// Parse message block m (16 × u64)
let mut m = [0u64; 16];
input[68..196]
.chunks_exact(8)
.enumerate()
.for_each(|(i, chunk)| {
m[i] = u64::from_le_bytes(chunk.try_into().unwrap());
});

// Parse offset counters
let t_0 = u64::from_le_bytes(input[196..204].try_into().unwrap());
let t_1 = u64::from_le_bytes(input[204..212].try_into().unwrap());

crypto_provider().blake2_compress(rounds, &mut h, &m, &[t_0, t_1], f);

let mut out = [0u8; 64];
for (i, h) in (0..64).step_by(8).zip(h.iter()) {
out[i..i + 8].copy_from_slice(&h.to_le_bytes());
}

Ok(PrecompileOutput::new(gas_used, out.into()))
}

#[cfg(test)]
mod tests {
use super::*;
use primitives::hex;
use std::time::Instant;

#[test]
fn perfblake2() {
let input = [hex!("0000040048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b616162636465666768696a6b6c6d6e6f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")
,hex!("0000020048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")
,hex!("0000004048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")];

let time = Instant::now();
for i in 0..3000 {
let _ = run(&input[i % 3], u64::MAX).unwrap();
}
println!("duration: {:?}", time.elapsed());
}
}
113 changes: 113 additions & 0 deletions crates/precompile/src/modexp/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! Pure cryptographic implementation for modular exponentiation.
//!
//! This module isolates the modexp computation from the precompile runner,
//! containing only the mathematical implementation without EVM-specific concerns.

use std::vec::Vec;

#[cfg(feature = "gmp")]
/// GMP-based modular exponentiation implementation
pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec<u8> {
use core::ffi::c_void;
use core::mem::MaybeUninit;
use gmp_mpfr_sys::gmp;

struct Mpz(gmp::mpz_t);

impl Mpz {
fn new() -> Self {
unsafe {
let mut inner = MaybeUninit::<gmp::mpz_t>::uninit();
gmp::mpz_init(inner.as_mut_ptr());
Self(inner.assume_init())
}
}

fn as_ptr(&self) -> *const gmp::mpz_t {
&self.0
}

fn as_mut_ptr(&mut self) -> *mut gmp::mpz_t {
&mut self.0
}

fn set_from_be_bytes(&mut self, bytes: &[u8]) {
unsafe {
if bytes.is_empty() {
gmp::mpz_set_ui(self.as_mut_ptr(), 0);
return;
}

gmp::mpz_import(
self.as_mut_ptr(),
bytes.len(),
1,
1,
1,
0,
bytes.as_ptr() as *const c_void,
);
}
}

fn to_be_bytes(&self) -> Vec<u8> {
unsafe {
if gmp::mpz_sgn(self.as_ptr()) == 0 {
return Vec::new();
}

let bits = gmp::mpz_sizeinbase(self.as_ptr(), 2);
let mut output = vec![0u8; bits.div_ceil(8)];
let mut count: usize = 0;
gmp::mpz_export(
output.as_mut_ptr() as *mut c_void,
&mut count,
1,
1,
1,
0,
self.as_ptr(),
);
output.truncate(count);
output
}
}
}

impl Drop for Mpz {
fn drop(&mut self) {
unsafe {
gmp::mpz_clear(self.as_mut_ptr());
}
}
}

let mut base_int = Mpz::new();
let mut exp_int = Mpz::new();
let mut mod_int = Mpz::new();
let mut result = Mpz::new();

base_int.set_from_be_bytes(base);
exp_int.set_from_be_bytes(exponent);
mod_int.set_from_be_bytes(modulus);

unsafe {
if gmp::mpz_sgn(mod_int.as_ptr()) == 0 {
return Vec::new();
}

gmp::mpz_powm(
result.as_mut_ptr(),
base_int.as_ptr(),
exp_int.as_ptr(),
mod_int.as_ptr(),
);
}

result.to_be_bytes()
}

#[cfg(not(feature = "gmp"))]
pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec<u8> {
aurora_engine_modexp::modexp(base, exponent, modulus)
}
Loading