diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb348ab25f..e252d89b4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,11 @@ jobs: with: toolchain: ${{ matrix.rust }} - uses: Swatinem/rust-cache@v2 + - name: Install GMP (all-features) + if: contains(matrix.flags, '--all-features') + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - run: cargo test --workspace ${{ matrix.flags }} check-no-std: @@ -74,6 +79,10 @@ jobs: - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true + - name: Install GMP + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - name: cargo hack run: cargo hack check --feature-powerset --depth 1 @@ -86,6 +95,10 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: clippy + - name: Install GMP + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - run: cargo clippy --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings @@ -99,6 +112,10 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rust-docs + - name: Install GMP + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - run: cargo doc --workspace --all-features --no-deps --document-private-items env: RUSTDOCFLAGS: "--cfg docsrs -D warnings" diff --git a/Cargo.lock b/Cargo.lock index b630d6d81f..b056d715b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1026,12 +1026,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - [[package]] name = "backtrace" version = "0.3.75" @@ -3802,13 +3796,13 @@ dependencies = [ "c-kzg", "cfg-if", "codspeed-criterion-compat", + "gmp-mpfr-sys", "k256", "p256", "rand 0.9.2", "revm-primitives", "ripemd", "rstest", - "rug", "secp256k1 0.31.1", "sha2", "substrate-bn", @@ -3932,18 +3926,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "rug" -version = "1.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad2e973fe3c3214251a840a621812a4f40468da814b1a3d6947d433c2af11f" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", - "libm", -] - [[package]] name = "ruint" version = "1.16.0" diff --git a/Cargo.toml b/Cargo.toml index 1bfb122ee9..867a136394 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ ark-ff = { version = "0.5", default-features = false } ark-serialize = { version = "0.5", default-features = false } ark-std = { version = "0.5", default-features = false } aurora-engine-modexp = { version = "1.2", default-features = false } -rug = { version = "1.28.0", default-features = false } +gmp-mpfr-sys = { version = "1.6", default-features = false } blst = "0.3.15" bn = { package = "substrate-bn", version = "0.6", default-features = false } c-kzg = { version = "2.1.4", default-features = false } diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index b17b1b3dba..0ee92291b8 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -23,8 +23,8 @@ primitives.workspace = true # modexp precompiles aurora-engine-modexp.workspace = true -# gmp wrapper -rug = { workspace = true, features = ["integer"], optional = true } +# gmp ffi +gmp-mpfr-sys = { workspace = true, default-features = false, optional = true } # ecRecover k256 = { workspace = true, features = ["ecdsa"] } @@ -75,6 +75,7 @@ rstest.workspace = true [features] default = ["std", "secp256k1", "blst", "c-kzg", "portable"] + std = [ "primitives/std", "k256/std", @@ -90,7 +91,6 @@ std = [ "ark-serialize/std", "ark-std/std", "p256/std", - "rug?/std", ] hashbrown = ["primitives/hashbrown"] asm-keccak = ["primitives/asm-keccak"] @@ -115,9 +115,9 @@ blst = ["dep:blst"] # Enables the substrate implementation of eip1962 bn = ["dep:bn"] -# Use rug (that wraps gmp) for modexp precompile. -# It is faster library but licences as GPL code, if enabled please make sure to follow the license. -gmp = ["dep:rug"] +# Use GMP (LGPL) for modexp precompile via gmp-mpfr-sys. +# This feature links to system GMP using the experimental `use-system-libs` flag. +gmp = ["dep:gmp-mpfr-sys", "gmp-mpfr-sys/use-system-libs"] [[bench]] name = "bench" diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index 7a4fde3ed2..2c209754bd 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -27,20 +27,103 @@ pub const OSAKA: Precompile = #[cfg(feature = "gmp")] /// GMP-based modular exponentiation implementation pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec { - use rug::{integer::Order::Msf, Integer}; - // Convert byte slices to GMP integers - let base_int = Integer::from_digits(base, Msf); - let exp_int = Integer::from_digits(exponent, Msf); - let mod_int = Integer::from_digits(modulus, Msf); - - // Perform modular exponentiation using GMP's pow_mod - let result = base_int.pow_mod(&exp_int, &mod_int).unwrap_or_default(); - - // Convert result back to bytes - let byte_count = result.significant_bits().div_ceil(8); - let mut output = vec![0u8; byte_count as usize]; - result.write_digits(&mut output, Msf); - output + 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::::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 { + 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"))] diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 3946ad1ca1..eff908e784 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -115,8 +115,7 @@ asm-sha2 = ["precompile/asm-sha2"] # Binary can be executed on all systems. portable = ["precompile/portable"] -# use gmp for modexp precompile. -# It is faster library but licenced as GPL code, if enabled please make sure to follow the license. +# Use GMP for modexp precompile (LGPL; dynamically linked via system libs). gmp = ["precompile/gmp"] # Statetest types for running ethereum tests