Skip to content

Commit 31176c0

Browse files
refcellclabby
authored andcommitted
feat(client): EIP-2537 BLS12-381 Curve Precompile Acceleration (op-rs#960)
Co-authored-by: clabby <ben@clab.by>
1 parent 29229c9 commit 31176c0

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

bin/client/src/precompiles/bls12.rs

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//! Contains the accelerated precompile for the BLS12-381 curve.
2+
//!
3+
//! BLS12-381 is introduced in [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537).
4+
//!
5+
//! For constants and logic, see the [revm implementation].
6+
//!
7+
//! [revm implementation]: https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bls12_381/pairing.rs
8+
9+
use crate::{HINT_WRITER, ORACLE_READER};
10+
use alloc::{string::ToString, vec::Vec};
11+
use alloy_primitives::{address, keccak256, Address, Bytes};
12+
use kona_preimage::{
13+
errors::PreimageOracleError, HintWriterClient, PreimageKey, PreimageKeyType,
14+
PreimageOracleClient,
15+
};
16+
use kona_proof::{errors::OracleProviderError, HintType};
17+
use revm::{
18+
precompile::{Error as PrecompileError, Precompile, PrecompileResult, PrecompileWithAddress},
19+
primitives::PrecompileOutput,
20+
};
21+
22+
/// The max pairing size for BLS12-381 input given a 20M gas limit.
23+
const BLS12_MAX_PAIRING_SIZE_ISTHMUS: usize = 235_008;
24+
25+
/// The address of the BLS12-381 pairing check precompile.
26+
const BLS12_PAIRING_CHECK: Address = address!("0x000000000000000000000000000000000000000f");
27+
28+
/// Input length of pairing operation.
29+
const INPUT_LENGTH: usize = 384;
30+
31+
/// Multiplier gas fee for BLS12-381 pairing operation.
32+
const PAIRING_MULTIPLIER_BASE: u64 = 32600;
33+
34+
/// Offset gas fee for BLS12-381 pairing operation.
35+
const PAIRING_OFFSET_BASE: u64 = 37700;
36+
37+
/// The address of the BLS12-381 pairing precompile.
38+
pub(crate) const FPVM_BLS12_PAIRING_ISTHMUS: PrecompileWithAddress =
39+
PrecompileWithAddress(BLS12_PAIRING_CHECK, Precompile::Standard(fpvm_bls12_pairing_isthmus));
40+
41+
/// Performs an FPVM-accelerated BLS12-381 pairing check.
42+
fn fpvm_bls12_pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult {
43+
let input_len = input.len();
44+
if input_len % INPUT_LENGTH != 0 {
45+
return Err(PrecompileError::Other(alloc::format!(
46+
"Pairing input length should be multiple of {INPUT_LENGTH}, was {input_len}"
47+
))
48+
.into());
49+
}
50+
51+
let k = input_len / INPUT_LENGTH;
52+
let required_gas: u64 = PAIRING_MULTIPLIER_BASE * k as u64 + PAIRING_OFFSET_BASE;
53+
if required_gas > gas_limit {
54+
return Err(PrecompileError::OutOfGas.into());
55+
}
56+
57+
let result_data = kona_proof::block_on(async move {
58+
// Write the hint for the ecrecover precompile run.
59+
let hint_data = &[BLS12_PAIRING_CHECK.as_ref(), input.as_ref()];
60+
HINT_WRITER
61+
.write(&HintType::L1Precompile.encode_with(hint_data))
62+
.await
63+
.map_err(OracleProviderError::Preimage)?;
64+
65+
// Construct the key hash for the ecrecover precompile run.
66+
let raw_key_data = hint_data.iter().copied().flatten().copied().collect::<Vec<u8>>();
67+
let key_hash = keccak256(&raw_key_data);
68+
69+
// Fetch the result of the ecrecover precompile run from the host.
70+
let result_data = ORACLE_READER
71+
.get(PreimageKey::new(*key_hash, PreimageKeyType::Precompile))
72+
.await
73+
.map_err(OracleProviderError::Preimage)?;
74+
75+
// Ensure we've received valid result data.
76+
if result_data.is_empty() {
77+
return Err(OracleProviderError::Preimage(PreimageOracleError::Other(
78+
"Invalid result data".to_string(),
79+
)));
80+
}
81+
82+
// Ensure we've not received an error from the host.
83+
if result_data[0] == 0 {
84+
return Err(OracleProviderError::Preimage(PreimageOracleError::Other(
85+
"Error executing ecrecover precompile in host".to_string(),
86+
)));
87+
}
88+
89+
// Return the result data.
90+
Ok(result_data[1..].to_vec())
91+
})
92+
.map_err(|e| PrecompileError::Other(e.to_string()))?;
93+
94+
Ok(PrecompileOutput::new(required_gas, result_data.into()))
95+
}
96+
97+
/// Performs an FPVM-accelerated `bls12` pairing check precompile call
98+
/// after the Isthmus Hardfork.
99+
fn fpvm_bls12_pairing_isthmus(input: &Bytes, gas_limit: u64) -> PrecompileResult {
100+
if input.len() > BLS12_MAX_PAIRING_SIZE_ISTHMUS {
101+
return Err(PrecompileError::Other(alloc::format!(
102+
"Pairing input length must be at most {}",
103+
BLS12_MAX_PAIRING_SIZE_ISTHMUS
104+
))
105+
.into());
106+
}
107+
108+
fpvm_bls12_pairing(input, gas_limit)
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
use alloc::vec;
115+
116+
#[test]
117+
fn test_fpvm_bls12_pairing_isthmus_max_bytes() {
118+
let input = Bytes::from(vec![0u8; BLS12_MAX_PAIRING_SIZE_ISTHMUS + 1]);
119+
let gas_limit = PAIRING_MULTIPLIER_BASE;
120+
let err = PrecompileError::Other("Pairing input length must be at most 235008".to_string());
121+
assert_eq!(fpvm_bls12_pairing_isthmus(&input, gas_limit), Err(err.into()));
122+
}
123+
124+
#[test]
125+
fn test_fpvm_bls12_offset() {
126+
let input = Bytes::from(vec![0u8; INPUT_LENGTH + 1]);
127+
let gas_limit = PAIRING_OFFSET_BASE;
128+
let err = PrecompileError::Other(
129+
"Pairing input length should be multiple of 384, was 385".to_string(),
130+
);
131+
assert_eq!(fpvm_bls12_pairing(&input, gas_limit), Err(err.into()));
132+
}
133+
134+
#[test]
135+
fn test_fpvm_bls12_out_of_gas() {
136+
let input = Bytes::from(vec![0u8; INPUT_LENGTH * 2]);
137+
let gas_limit = PAIRING_MULTIPLIER_BASE - 1;
138+
assert_eq!(fpvm_bls12_pairing(&input, gas_limit), Err(PrecompileError::OutOfGas.into()));
139+
}
140+
}

bin/client/src/precompiles/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use revm::{
1111
State,
1212
};
1313

14+
mod bls12;
1415
mod bn128_pair;
1516
mod ecrecover;
1617
mod kzg_point_eval;
@@ -43,6 +44,10 @@ pub(crate) fn fpvm_handle_register<F, H>(
4344
ctx_precompiles.extend([bn128_pair::FPVM_ECPAIRING_GRANITE]);
4445
}
4546

47+
if spec_id.is_enabled_in(SpecId::ISTHMUS) {
48+
ctx_precompiles.extend([bls12::FPVM_BLS12_PAIRING_ISTHMUS]);
49+
}
50+
4651
ctx_precompiles
4752
});
4853
}

bin/host/src/fetcher/precompiles.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use revm::{
1111
pub(crate) const ACCELERATED_PRECOMPILES: &[PrecompileWithAddress] = &[
1212
precompile::secp256k1::ECRECOVER, // ecRecover
1313
precompile::bn128::pair::ISTANBUL, // ecPairing
14+
precompile::bls12_381::pairing::PRECOMPILE, // BLS12-381 pairing
1415
precompile::kzg_point_evaluation::POINT_EVALUATION, // KZG point evaluation
1516
];
1617

0 commit comments

Comments
 (0)