Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: blst reprice, remove g1/g2 mul #1981

Merged
merged 3 commits into from
Jan 9, 2025
Merged
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
1 change: 0 additions & 1 deletion .github/workflows/ethereum-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ jobs:
ethtests/LegacyTests/Constantinople/GeneralStateTests/ \
ethtests/EIPTests/StateTests/stEIP1153-transientStorage/ \
ethtests/EIPTests/StateTests/stEIP4844-blobtransactions/ \
ethtests/EIPTests/StateTests/stEIP2537/ \
ethtests/EIPTests/StateTests/stEOF \
tests/eof_suite/eest/state_tests \
tests/pectra_devnet5/state_tests/prague/eip7623_increase_calldata_cost
Expand Down
128 changes: 1 addition & 127 deletions crates/precompile/src/bls12_381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,151 +3,25 @@ use crate::PrecompileWithAddress;
mod g1;
pub mod g1_add;
pub mod g1_msm;
pub mod g1_mul;
mod g2;
pub mod g2_add;
pub mod g2_msm;
pub mod g2_mul;
pub mod map_fp2_to_g2;
pub mod map_fp_to_g1;
mod msm;
pub mod msm;
pub mod pairing;
mod utils;

/// Returns the BLS12-381 precompiles with their addresses.
pub fn precompiles() -> impl Iterator<Item = PrecompileWithAddress> {
[
g1_add::PRECOMPILE,
g1_mul::PRECOMPILE,
g1_msm::PRECOMPILE,
g2_add::PRECOMPILE,
g2_mul::PRECOMPILE,
g2_msm::PRECOMPILE,
pairing::PRECOMPILE,
map_fp_to_g1::PRECOMPILE,
map_fp2_to_g2::PRECOMPILE,
]
.into_iter()
}

#[cfg(test)]
mod test {
use super::g1_add;
use super::g1_msm;
use super::g1_mul;
use super::g2_add;
use super::g2_msm;
use super::g2_mul;
use super::map_fp2_to_g2;
use super::map_fp_to_g1;
use super::msm::msm_required_gas;
use super::pairing;
use crate::PrecompileResult;
use eyre::Result;
use primitives::{hex::FromHex, Bytes};
use rstest::rstest;
use serde_derive::{Deserialize, Serialize};
use std::{fs, path::Path};

/// Test vector structure for BLS12-381 precompile tests
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct TestVector {
input: String,
expected: Option<String>,
name: String,
gas: Option<u64>,
expected_error: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct TestVectors(Vec<TestVector>);

fn load_test_vectors<P: AsRef<Path>>(path: P) -> Result<TestVectors> {
let file_contents = fs::read_to_string(path)?;
Ok(serde_json::from_str(&file_contents)?)
}

#[rstest]
#[case::fail_g1_add(g1_add::g1_add, "fail-add_G1_bls.json")]
#[case::fail_g1_mul(g1_mul::g1_mul, "fail-mul_G1_bls.json")]
#[case::fail_g1_msm(g1_msm::g1_msm, "fail-multiexp_G1_bls.json")]
#[case::fail_g2_add(g2_add::g2_add, "fail-add_G2_bls.json")]
#[case::fail_g2_mul(g2_mul::g2_mul, "fail-mul_G2_bls.json")]
#[case::fail_g2_msm(g2_msm::g2_msm, "fail-multiexp_G2_bls.json")]
#[case::fail_pairing(pairing::pairing, "fail-pairing_check_bls.json")]
#[case::fail_map_fp_to_g1(map_fp_to_g1::map_fp_to_g1, "fail-map_fp_to_G1_bls.json")]
#[case::fail_map_fp2_to_g2(map_fp2_to_g2::map_fp2_to_g2, "fail-map_fp2_to_G2_bls.json")]
#[case::g1_add(g1_add::g1_add, "add_G1_bls.json")]
#[case::g1_mul(g1_mul::g1_mul, "mul_G1_bls.json")]
#[case::g1_msm(g1_msm::g1_msm, "multiexp_G1_bls.json")]
#[case::g2_add(g2_add::g2_add, "add_G2_bls.json")]
#[case::g2_mul(g2_mul::g2_mul, "mul_G2_bls.json")]
#[case::g2_msm(g2_msm::g2_msm, "multiexp_G2_bls.json")]
#[case::pairing(pairing::pairing, "pairing_check_bls.json")]
#[case::map_fp_to_g1(map_fp_to_g1::map_fp_to_g1, "map_fp_to_G1_bls.json")]
#[case::map_fp2_to_g2(map_fp2_to_g2::map_fp2_to_g2, "map_fp2_to_G2_bls.json")]
fn test_bls(
#[case] precompile: fn(input: &Bytes, gas_limit: u64) -> PrecompileResult,
#[case] file_name: &str,
) {
let test_vectors = load_test_vectors(format!("test-vectors/{file_name}"))
.unwrap_or_else(|e| panic!("Failed to load test vectors from {file_name}: {e}"));

for vector in test_vectors.0 {
let test_name = format!("{file_name}/{}", vector.name);
let input = Bytes::from_hex(vector.input.clone()).unwrap_or_else(|e| {
panic!(
"could not deserialize input {} as hex in {test_name}: {e}",
&vector.input
)
});
let target_gas: u64 = 30_000_000;
let res = precompile(&input, target_gas);
if let Some(expected_error) = vector.expected_error {
assert!(res.is_err(), "expected error {expected_error} didn't happen in {test_name}, got result {res:?}");
} else {
let Some(gas) = vector.gas else {
panic!("gas is missing in {test_name}");
};
let outcome =
res.unwrap_or_else(|e| panic!("precompile call failed for {test_name}: {e}"));
assert_eq!(
gas, outcome.gas_used,
"expected gas: {}, actual gas: {} in {test_name}",
gas, outcome.gas_used
);
let Some(expected) = vector.expected else {
panic!("expected output is missing in {test_name}");
};
let expected_output = Bytes::from_hex(expected).unwrap();
assert_eq!(
expected_output, outcome.bytes,
"expected output: {expected_output}, actual output: {:?} in {test_name}",
outcome.bytes
);
}
}
}

#[rstest]
#[case::g1_empty(0, g1_mul::BASE_GAS_FEE, 0)]
#[case::g1_one_item(160, g1_mul::BASE_GAS_FEE, 14400)]
#[case::g1_two_items(320, g1_mul::BASE_GAS_FEE, 21312)]
#[case::g1_ten_items(1600, g1_mul::BASE_GAS_FEE, 50760)]
#[case::g1_sixty_four_items(10240, g1_mul::BASE_GAS_FEE, 170496)]
#[case::g1_one_hundred_twenty_eight_items(20480, g1_mul::BASE_GAS_FEE, 267264)]
#[case::g1_one_hundred_twenty_nine_items(20640, g1_mul::BASE_GAS_FEE, 269352)]
#[case::g1_two_hundred_fifty_six_items(40960, g1_mul::BASE_GAS_FEE, 534528)]
fn test_g1_msm_required_gas(
#[case] input_len: usize,
#[case] multiplication_cost: u64,
#[case] expected_output: u64,
) {
let k = input_len / g1_mul::INPUT_LENGTH;

let actual_output = msm_required_gas(k, multiplication_cost);

assert_eq!(expected_output, actual_output);
}
}
2 changes: 1 addition & 1 deletion crates/precompile/src/bls12_381/g1_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub const PRECOMPILE: PrecompileWithAddress =
/// BLS12_G1ADD precompile address.
pub const ADDRESS: u64 = 0x0b;
/// Base gas fee for BLS12-381 g1_add operation.
const BASE_GAS_FEE: u64 = 500;
const BASE_GAS_FEE: u64 = 375;

/// Input length of g1_add operation.
const INPUT_LENGTH: usize = 256;
Expand Down
36 changes: 25 additions & 11 deletions crates/precompile/src/bls12_381/g1_msm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::{
g1::{encode_g1_point, extract_g1_input, G1_INPUT_ITEM_LENGTH},
g1_mul,
msm::msm_required_gas,
utils::{extract_scalar_input, NBITS, SCALAR_LENGTH},
};
Expand All @@ -14,7 +13,24 @@ pub const PRECOMPILE: PrecompileWithAddress =
PrecompileWithAddress(u64_to_address(ADDRESS), g1_msm);

/// BLS12_G1MSM precompile address.
pub const ADDRESS: u64 = 0x0d;
pub const ADDRESS: u64 = 0x0c;

/// Base gas fee for BLS12-381 g1_mul operation.
pub const BASE_GAS_FEE: u64 = 12000;

/// Input length of g1_mul operation.
pub const INPUT_LENGTH: usize = 160;

/// Discounts table for G1 MSM as a vector of pairs `[k, discount]`.
pub static DISCOUNT_TABLE: [u16; 128] = [
1000, 949, 848, 797, 764, 750, 738, 728, 719, 712, 705, 698, 692, 687, 682, 677, 673, 669, 665,
661, 658, 654, 651, 648, 645, 642, 640, 637, 635, 632, 630, 627, 625, 623, 621, 619, 617, 615,
613, 611, 609, 608, 606, 604, 603, 601, 599, 598, 596, 595, 593, 592, 591, 589, 588, 586, 585,
584, 582, 581, 580, 579, 577, 576, 575, 574, 573, 572, 570, 569, 568, 567, 566, 565, 564, 563,
562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551, 550, 549, 548, 547, 547, 546, 545,
544, 543, 542, 541, 540, 540, 539, 538, 537, 536, 536, 535, 534, 533, 532, 532, 531, 530, 529,
528, 528, 527, 526, 525, 525, 524, 523, 522, 522, 521, 520, 520, 519,
];

/// Implements EIP-2537 G1MSM precompile.
/// G1 multi-scalar-multiplication call expects `160*k` bytes as an input that is interpreted
Expand All @@ -26,26 +42,24 @@ pub const ADDRESS: u64 = 0x0d;
/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-multiexponentiation>
pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult {
let input_len = input.len();
if input_len == 0 || input_len % g1_mul::INPUT_LENGTH != 0 {
if input_len == 0 || input_len % INPUT_LENGTH != 0 {
return Err(PrecompileError::Other(format!(
"G1MSM input length should be multiple of {}, was {}",
g1_mul::INPUT_LENGTH,
input_len
INPUT_LENGTH, input_len
))
.into());
}

let k = input_len / g1_mul::INPUT_LENGTH;
let required_gas = msm_required_gas(k, g1_mul::BASE_GAS_FEE);
let k = input_len / INPUT_LENGTH;
let required_gas = msm_required_gas(k, &DISCOUNT_TABLE, BASE_GAS_FEE);
if required_gas > gas_limit {
return Err(PrecompileError::OutOfGas.into());
}

let mut g1_points: Vec<blst_p1> = Vec::with_capacity(k);
let mut scalars: Vec<u8> = Vec::with_capacity(k * SCALAR_LENGTH);
for i in 0..k {
let slice =
&input[i * g1_mul::INPUT_LENGTH..i * g1_mul::INPUT_LENGTH + G1_INPUT_ITEM_LENGTH];
let slice = &input[i * INPUT_LENGTH..i * INPUT_LENGTH + G1_INPUT_ITEM_LENGTH];

// BLST batch API for p1_affines blows up when you pass it a point at infinity, so we must
// filter points at infinity (and their corresponding scalars) from the input.
Expand All @@ -65,8 +79,8 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult {

scalars.extend_from_slice(
&extract_scalar_input(
&input[i * g1_mul::INPUT_LENGTH + G1_INPUT_ITEM_LENGTH
..i * g1_mul::INPUT_LENGTH + G1_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
&input[i * INPUT_LENGTH + G1_INPUT_ITEM_LENGTH
..i * INPUT_LENGTH + G1_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
)?
.b,
);
Expand Down
61 changes: 0 additions & 61 deletions crates/precompile/src/bls12_381/g1_mul.rs

This file was deleted.

4 changes: 2 additions & 2 deletions crates/precompile/src/bls12_381/g2_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use primitives::Bytes;
pub const PRECOMPILE: PrecompileWithAddress =
PrecompileWithAddress(u64_to_address(ADDRESS), g2_add);
/// BLS12_G2ADD precompile address.
pub const ADDRESS: u64 = 0x0e;
pub const ADDRESS: u64 = 0x0d;
/// Base gas fee for BLS12-381 g2_add operation.
const BASE_GAS_FEE: u64 = 800;
const BASE_GAS_FEE: u64 = 600;

/// Input length of g2_add operation.
const INPUT_LENGTH: usize = 512;
Expand Down
36 changes: 25 additions & 11 deletions crates/precompile/src/bls12_381/g2_msm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::{
g2::{encode_g2_point, extract_g2_input, G2_INPUT_ITEM_LENGTH},
g2_mul,
msm::msm_required_gas,
utils::{extract_scalar_input, NBITS, SCALAR_LENGTH},
};
Expand All @@ -14,7 +13,24 @@ pub const PRECOMPILE: PrecompileWithAddress =
PrecompileWithAddress(u64_to_address(ADDRESS), g2_msm);

/// BLS12_G2MSM precompile address.
pub const ADDRESS: u64 = 0x10;
pub const ADDRESS: u64 = 0x0e;

/// Base gas fee for BLS12-381 g2_mul operation.
pub const BASE_GAS_FEE: u64 = 22500;

/// Input length of g2_mul operation.
pub const INPUT_LENGTH: usize = 288;

// Discounts table for G2 MSM as a vector of pairs `[k, discount]`:
pub static DISCOUNT_TABLE: [u16; 128] = [
1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704,
699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, 632,
629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, 595, 593,
592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, 570, 569, 568,
567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 552, 551, 550, 549,
548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, 537, 536, 535, 535, 534,
533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, 524, 524,
];

/// Implements EIP-2537 G2MSM precompile.
/// G2 multi-scalar-multiplication call expects `288*k` bytes as an input that is interpreted
Expand All @@ -26,26 +42,24 @@ pub const ADDRESS: u64 = 0x10;
/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-multiexponentiation>
pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult {
let input_len = input.len();
if input_len == 0 || input_len % g2_mul::INPUT_LENGTH != 0 {
if input_len == 0 || input_len % INPUT_LENGTH != 0 {
return Err(PrecompileError::Other(format!(
"G2MSM input length should be multiple of {}, was {}",
g2_mul::INPUT_LENGTH,
input_len
INPUT_LENGTH, input_len
))
.into());
}

let k = input_len / g2_mul::INPUT_LENGTH;
let required_gas = msm_required_gas(k, g2_mul::BASE_GAS_FEE);
let k = input_len / INPUT_LENGTH;
let required_gas = msm_required_gas(k, &DISCOUNT_TABLE, BASE_GAS_FEE);
if required_gas > gas_limit {
return Err(PrecompileError::OutOfGas.into());
}

let mut g2_points: Vec<blst_p2> = Vec::with_capacity(k);
let mut scalars: Vec<u8> = Vec::with_capacity(k * SCALAR_LENGTH);
for i in 0..k {
let slice =
&input[i * g2_mul::INPUT_LENGTH..i * g2_mul::INPUT_LENGTH + G2_INPUT_ITEM_LENGTH];
let slice = &input[i * INPUT_LENGTH..i * INPUT_LENGTH + G2_INPUT_ITEM_LENGTH];
// BLST batch API for p2_affines blows up when you pass it a point at infinity, so we must
// filter points at infinity (and their corresponding scalars) from the input.
if slice.iter().all(|i| *i == 0) {
Expand All @@ -65,8 +79,8 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult {

scalars.extend_from_slice(
&extract_scalar_input(
&input[i * g2_mul::INPUT_LENGTH + G2_INPUT_ITEM_LENGTH
..i * g2_mul::INPUT_LENGTH + G2_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
&input[i * INPUT_LENGTH + G2_INPUT_ITEM_LENGTH
..i * INPUT_LENGTH + G2_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
)?
.b,
);
Expand Down
Loading
Loading