use super::{
utils::{remove_g1_padding, remove_g2_padding},
PairingPair,
};
use crate::{
bls12_381_const::{
PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH,
PAIRING_MULTIPLIER_BASE, PAIRING_OFFSET_BASE,
},
crypto, eth_precompile_fn, EthPrecompileOutput, EthPrecompileResult, Precompile,
PrecompileHalt, PrecompileId,
};
use primitives::B256;
use std::vec::Vec;
eth_precompile_fn!(pairing_precompile, pairing);
pub const PRECOMPILE: Precompile = Precompile::new(
PrecompileId::Bls12Pairing,
PAIRING_ADDRESS,
pairing_precompile,
);
pub fn pairing(input: &[u8], gas_limit: u64) -> EthPrecompileResult {
let input_len = input.len();
if input_len == 0 || !input_len.is_multiple_of(PAIRING_INPUT_LENGTH) {
return Err(PrecompileHalt::Bls12381PairingInputLength);
}
let k = input_len / PAIRING_INPUT_LENGTH;
let required_gas: u64 = PAIRING_MULTIPLIER_BASE * k as u64 + PAIRING_OFFSET_BASE;
if required_gas > gas_limit {
return Err(PrecompileHalt::OutOfGas);
}
let mut pairs: Vec<PairingPair> = Vec::with_capacity(k);
for i in 0..k {
let encoded_g1_element =
&input[i * PAIRING_INPUT_LENGTH..i * PAIRING_INPUT_LENGTH + PADDED_G1_LENGTH];
let encoded_g2_element = &input[i * PAIRING_INPUT_LENGTH + PADDED_G1_LENGTH
..i * PAIRING_INPUT_LENGTH + PADDED_G1_LENGTH + PADDED_G2_LENGTH];
let [a_x, a_y] = remove_g1_padding(encoded_g1_element)?;
let [b_x_0, b_x_1, b_y_0, b_y_1] = remove_g2_padding(encoded_g2_element)?;
pairs.push(((*a_x, *a_y), (*b_x_0, *b_x_1, *b_y_0, *b_y_1)));
}
let result = crypto().bls12_381_pairing_check(&pairs)?;
let result = if result { 1 } else { 0 };
Ok(EthPrecompileOutput::new(
required_gas,
B256::with_last_byte(result).into(),
))
}