Skip to content

Commit

Permalink
Rust API for BLS signatures and EVM precompiles (#387)
Browse files Browse the repository at this point in the history
* [rust] update Rust bindings for new C API symbols

Done by running:

```sh
sh scripts/gen_rust_bindings.sh
```
from Constantine's root dir

* add initial BLS signature Rust wrapper

* add Display trait to `ctt_eth_*` enums

NOTE: I'm not sure right now whether `lib.rs` is *also* generated by
`bindgen` or not? I would guess not? Because in that case this change
wouldn't be a good idea obviously (overwritten on next wrapper update)

* clean up some compiler warnings

* [tests] port BLS signature tests cases to Rust

* add Rust package for EVM precompiles

* [tests] port test cases for EVM precompiles to Rust

* update rust bindings to C headers
  • Loading branch information
Vindaar authored Jun 23, 2024
1 parent 9fe5e49 commit af7fa73
Show file tree
Hide file tree
Showing 10 changed files with 2,230 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ members = [
"constantine-rust/constantine-core",
"constantine-rust/constantine-halo2-zal",
"constantine-rust/constantine-ethereum-kzg",
"constantine-rust/constantine-ethereum-bls-sig",
"constantine-rust/constantine-ethereum-evm-precompiles",
]

# If Nim static library is compiled with Clang ThinLTO, enable it on Rust side
Expand Down
18 changes: 18 additions & 0 deletions constantine-rust/constantine-ethereum-bls-sig/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "constantine-ethereum-bls-sig"
version = "0.1.0"
edition = "2021"

authors = ["Mamy André-Ratsimbazafy"]
license = "MIT/Apache-2.0"
repository = "https://github.com/mratsim/constantine"

[dependencies]
constantine-sys = { path = "../constantine-sys" }
constantine-core = { path = "../constantine-core" }

[dev-dependencies]
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = "1.0"
hex = { version = "0.4", default-features = false, features = ["serde"] }
glob = "0.3"
340 changes: 340 additions & 0 deletions constantine-rust/constantine-ethereum-bls-sig/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
//! Constantine
//! Copyright (c) 2018-2019 Status Research & Development GmbH
//! Copyright (c) 2020-Present Mamy André-Ratsimbazafy
//! Licensed and distributed under either of
//! * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
//! * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
//! at your option. This file may not be copied, modified, or distributed except according to those terms.

use constantine_core::Threadpool;
use constantine_sys::*;

// Create type aliases for the C types
pub type EthBlsSecKey = ctt_eth_bls_seckey;
pub type EthBlsPubKey = ctt_eth_bls_pubkey;
pub type EthBlsSignature = ctt_eth_bls_signature;

#[must_use]
pub fn deserialize_seckey(
skey: *mut EthBlsSecKey,
src: &[u8; 32]
) -> Result<bool, ctt_codec_scalar_status> {
unsafe {
let status = ctt_eth_bls_deserialize_seckey(
skey as *mut ctt_eth_bls_seckey,
src.as_ptr() as *const byte,
);
match status {
ctt_codec_scalar_status::cttCodecScalar_Success => Ok(true),
_ => Err(status),
}
}
}

#[must_use]
pub fn sha256_hash(
message: &[u8], clear_memory: bool
) -> [u8; 32] {
let mut result = [0u8; 32];
unsafe {
ctt_sha256_hash(
result.as_mut_ptr() as *mut byte,
message.as_ptr() as *const byte,
message.len() as isize,
clear_memory as bool
)
}
return result
}

pub fn derive_pubkey(
pkey: *mut EthBlsPubKey,
skey: EthBlsSecKey
) {
unsafe {
ctt_eth_bls_derive_pubkey(
pkey as *mut ctt_eth_bls_pubkey,
&skey as *const ctt_eth_bls_seckey,
);
}
}


#[must_use]
pub fn pubkeys_are_equal(
pkey1: EthBlsPubKey,
pkey2: EthBlsPubKey,
) -> bool {
unsafe {
return ctt_eth_bls_pubkeys_are_equal(
&pkey1 as *const ctt_eth_bls_pubkey,
&pkey2 as *const ctt_eth_bls_pubkey,
) as bool;
}
}

#[must_use]
pub fn signatures_are_equal(
pkey1: EthBlsSignature,
pkey2: EthBlsSignature,
) -> bool {
unsafe {
return ctt_eth_bls_signatures_are_equal(
&pkey1 as *const ctt_eth_bls_signature,
&pkey2 as *const ctt_eth_bls_signature,
) as bool;
}
}

#[must_use]
pub fn serialize_pubkey_compressed(
pkey: &EthBlsPubKey,
dst: &mut [u8; 48],
) -> Result<bool, ctt_codec_ecc_status> {
unsafe {
let status = ctt_eth_bls_serialize_pubkey_compressed(
dst.as_mut_ptr() as *mut byte,
pkey as *const ctt_eth_bls_pubkey,
);
match status {
ctt_codec_ecc_status::cttCodecEcc_Success => Ok(true),
_ => Err(status)
}
}

}

#[must_use]
pub fn serialize_signature_compressed(
sig: &EthBlsSignature,
dst: &mut [u8; 96],
) -> Result<bool, ctt_codec_ecc_status> {
unsafe {
let status = ctt_eth_bls_serialize_signature_compressed(
dst.as_mut_ptr() as *mut byte,
sig as *const ctt_eth_bls_signature,
);
match status {
ctt_codec_ecc_status::cttCodecEcc_Success => Ok(true),
_ => Err(status)
}
}

}

#[must_use]
pub fn deserialize_pubkey_compressed(
pkey: *mut EthBlsPubKey,
src: &[u8; 48],
) -> Result<bool, ctt_codec_ecc_status> {
unsafe {
let status = ctt_eth_bls_deserialize_pubkey_compressed(
pkey as *mut ctt_eth_bls_pubkey,
src.as_ptr() as *const byte,
);
match status {
ctt_codec_ecc_status::cttCodecEcc_Success => Ok(true),
ctt_codec_ecc_status::cttCodecEcc_PointAtInfinity => Ok(true),
_ => Err(status)
}
}
}

#[must_use]
pub fn deserialize_signature_compressed(
sig: *mut EthBlsSignature,
src: &[u8; 96],
) -> Result<bool, ctt_codec_ecc_status> {
unsafe {
let status = ctt_eth_bls_deserialize_signature_compressed(
sig as *mut ctt_eth_bls_signature,
src.as_ptr() as *const byte,
);
match status {
ctt_codec_ecc_status::cttCodecEcc_Success => Ok(true),
ctt_codec_ecc_status::cttCodecEcc_PointAtInfinity => Ok(true),
_ => Err(status)
}
}
}

#[must_use]
pub fn deserialize_seckey_compressed(
skey: *mut EthBlsSecKey,
src: &[u8; 32],
) -> Result<bool, ctt_codec_scalar_status> {
unsafe {
let status = ctt_eth_bls_deserialize_seckey(
skey as *mut ctt_eth_bls_seckey,
src.as_ptr() as *const byte,
);
match status {
ctt_codec_scalar_status::cttCodecScalar_Success => Ok(true),
_ => Err(status)
}
}
}

pub fn sign(
sig: *mut EthBlsSignature,
skey: EthBlsSecKey,
message: &[u8]
) {
unsafe {
ctt_eth_bls_sign(
sig as *mut ctt_eth_bls_signature,
&skey as *const ctt_eth_bls_seckey,
message.as_ptr() as *const byte,
message.len() as isize
)
}
}

#[must_use]
pub fn verify(
pkey: &EthBlsPubKey,
message: &[u8],
sig: EthBlsSignature,
) -> Result<bool, ctt_eth_bls_status> {
unsafe {
let status = ctt_eth_bls_verify(
pkey as *const ctt_eth_bls_pubkey,
message.as_ptr() as *const byte,
message.len() as isize,
&sig as *const ctt_eth_bls_signature
);
match status {
ctt_eth_bls_status::cttEthBls_Success => Ok(true),
_ => Err(status)
}
}
}

#[must_use]
pub fn fast_aggregate_verify(
pubkeys: &[EthBlsPubKey],
message: &[u8],
signature: &EthBlsSignature,
) -> Result<bool, ctt_eth_bls_status> {
// TODO: do we really have to clone here, just to assign to CttSpan due to a *mut field?
//let spans = to_span(&mut msg);

unsafe {
let status = ctt_eth_bls_fast_aggregate_verify(
pubkeys.as_ptr() as *const ctt_eth_bls_pubkey,
pubkeys.len() as isize,
message.as_ptr() as *const byte,
message.len() as isize,
signature as *const ctt_eth_bls_signature,
);
match status {
ctt_eth_bls_status::cttEthBls_Success => Ok(true),
_ => Err(status)
}
}
}

// Define a byte compatible version of ctt_span so that we have
// access to the fields
#[repr(C)]
pub struct CttSpan {
pub data: *mut u8,
pub len: usize,
}

#[must_use]
fn to_span(vec: &mut Vec<Vec<u8>>) -> Vec<CttSpan> {
let mut spans = Vec::with_capacity(vec.len());
for v in vec {
let span = CttSpan{
data: v.as_mut_ptr() as *mut byte,
len: v.len(),
};
spans.push(span);
}
return spans;
}

#[must_use]
pub fn aggregate_verify(
pubkeys: &[EthBlsPubKey],
messages: &Vec<Vec<u8>>,
aggregate_sig: &EthBlsSignature,
) -> Result<bool, ctt_eth_bls_status> {
// TODO: do we really have to clone here, just to assign to CttSpan due to a *mut field?
// Funny irony, that we can hand (nested) pointers from Rust (contrary to Go), but pointer
// sanity logic makes us have to copy anyway?
let mut msg = messages.clone();
let spans = to_span(&mut msg);

unsafe {
let status = ctt_eth_bls_aggregate_verify(
pubkeys.as_ptr() as *const ctt_eth_bls_pubkey,
spans.as_ptr() as *const ctt_span,
pubkeys.len() as isize,
aggregate_sig as *const ctt_eth_bls_signature,
);
match status {
ctt_eth_bls_status::cttEthBls_Success => Ok(true),
_ => Err(status)
}
}
}

#[must_use]
pub fn batch_verify(
pubkeys: &[EthBlsPubKey],
messages: &Vec<Vec<u8>>,
signatures: &[EthBlsSignature],
secure_random_bytes: &[u8; 32]
) -> Result<bool, ctt_eth_bls_status> {
// TODO: do we really have to clone here, just to assign to CttSpan due to a *mut field?
// Funny irony, that we can hand (nested) pointers from Rust (contrary to Go), but pointer
// sanity logic makes us have to copy anyway?
let mut msg = messages.clone();
let spans = to_span(&mut msg);

unsafe {
let status = ctt_eth_bls_batch_verify(
pubkeys.as_ptr() as *const ctt_eth_bls_pubkey,
spans.as_ptr() as *const ctt_span,
signatures.as_ptr() as *const ctt_eth_bls_signature,
messages.len() as isize,
secure_random_bytes.as_ptr() as *const byte,
);
match status {
ctt_eth_bls_status::cttEthBls_Success => Ok(true),
_ => Err(status)
}
}
}

#[must_use]
pub fn batch_verify_parallel(
tp: &Threadpool,
pubkeys: &[EthBlsPubKey],
messages: &Vec<Vec<u8>>,
signatures: &[EthBlsSignature],
secure_random_bytes: &[u8; 32]
) -> Result<bool, ctt_eth_bls_status> {
// TODO: do we really have to clone here, just to assign to CttSpan due to a *mut field?
// Funny irony, that we can hand (nested) pointers from Rust (contrary to Go), but pointer
// sanity logic makes us have to copy anyway?
let mut msg = messages.clone();
let spans = to_span(&mut msg);

unsafe {
let status = ctt_eth_bls_batch_verify_parallel(
tp.get_private_context(),
pubkeys.as_ptr() as *const ctt_eth_bls_pubkey,
spans.as_ptr() as *const ctt_span,
signatures.as_ptr() as *const ctt_eth_bls_signature,
messages.len() as isize,
secure_random_bytes.as_ptr() as *const byte,
);
match status {
ctt_eth_bls_status::cttEthBls_Success => Ok(true),
_ => Err(status)
}
}
}
Loading

0 comments on commit af7fa73

Please sign in to comment.