revm_precompile/
bls12_381.rs

1use crate::PrecompileWithAddress;
2
3mod g1;
4pub mod g1_add;
5pub mod g1_msm;
6pub mod g1_mul;
7mod g2;
8pub mod g2_add;
9pub mod g2_msm;
10pub mod g2_mul;
11pub mod map_fp2_to_g2;
12pub mod map_fp_to_g1;
13mod msm;
14pub mod pairing;
15mod utils;
16
17/// Returns the BLS12-381 precompiles with their addresses.
18pub fn precompiles() -> impl Iterator<Item = PrecompileWithAddress> {
19    [
20        g1_add::PRECOMPILE,
21        g1_mul::PRECOMPILE,
22        g1_msm::PRECOMPILE,
23        g2_add::PRECOMPILE,
24        g2_mul::PRECOMPILE,
25        g2_msm::PRECOMPILE,
26        pairing::PRECOMPILE,
27        map_fp_to_g1::PRECOMPILE,
28        map_fp2_to_g2::PRECOMPILE,
29    ]
30    .into_iter()
31}
32
33#[cfg(test)]
34mod test {
35    use super::g1_add;
36    use super::g1_msm;
37    use super::g1_mul;
38    use super::g2_add;
39    use super::g2_msm;
40    use super::g2_mul;
41    use super::map_fp2_to_g2;
42    use super::map_fp_to_g1;
43    use super::msm::msm_required_gas;
44    use super::pairing;
45    use eyre::Result;
46    use revm_primitives::{hex::FromHex, Bytes, PrecompileResult};
47    use rstest::rstest;
48    use serde_derive::{Deserialize, Serialize};
49    use std::{fs, path::Path};
50
51    /// Test vector structure for BLS12-381 precompile tests.
52    #[derive(Serialize, Deserialize, Debug)]
53    #[serde(rename_all = "PascalCase")]
54    struct TestVector {
55        input: String,
56        expected: Option<String>,
57        name: String,
58        gas: Option<u64>,
59        expected_error: Option<String>,
60    }
61
62    #[derive(Serialize, Deserialize, Debug)]
63    struct TestVectors(Vec<TestVector>);
64
65    fn load_test_vectors<P: AsRef<Path>>(path: P) -> Result<TestVectors> {
66        let file_contents = fs::read_to_string(path)?;
67        Ok(serde_json::from_str(&file_contents)?)
68    }
69
70    #[rstest]
71    #[case::fail_g1_add(g1_add::g1_add, "fail-add_G1_bls.json")]
72    #[case::fail_g1_mul(g1_mul::g1_mul, "fail-mul_G1_bls.json")]
73    #[case::fail_g1_msm(g1_msm::g1_msm, "fail-multiexp_G1_bls.json")]
74    #[case::fail_g2_add(g2_add::g2_add, "fail-add_G2_bls.json")]
75    #[case::fail_g2_mul(g2_mul::g2_mul, "fail-mul_G2_bls.json")]
76    #[case::fail_g2_msm(g2_msm::g2_msm, "fail-multiexp_G2_bls.json")]
77    #[case::fail_pairing(pairing::pairing, "fail-pairing_check_bls.json")]
78    #[case::fail_map_fp_to_g1(map_fp_to_g1::map_fp_to_g1, "fail-map_fp_to_G1_bls.json")]
79    #[case::fail_map_fp2_to_g2(map_fp2_to_g2::map_fp2_to_g2, "fail-map_fp2_to_G2_bls.json")]
80    #[case::g1_add(g1_add::g1_add, "add_G1_bls.json")]
81    #[case::g1_mul(g1_mul::g1_mul, "mul_G1_bls.json")]
82    #[case::g1_msm(g1_msm::g1_msm, "multiexp_G1_bls.json")]
83    #[case::g2_add(g2_add::g2_add, "add_G2_bls.json")]
84    #[case::g2_mul(g2_mul::g2_mul, "mul_G2_bls.json")]
85    #[case::g2_msm(g2_msm::g2_msm, "multiexp_G2_bls.json")]
86    #[case::pairing(pairing::pairing, "pairing_check_bls.json")]
87    #[case::map_fp_to_g1(map_fp_to_g1::map_fp_to_g1, "map_fp_to_G1_bls.json")]
88    #[case::map_fp2_to_g2(map_fp2_to_g2::map_fp2_to_g2, "map_fp2_to_G2_bls.json")]
89    fn test_bls(
90        #[case] precompile: fn(input: &Bytes, gas_limit: u64) -> PrecompileResult,
91        #[case] file_name: &str,
92    ) {
93        let test_vectors = load_test_vectors(format!("test-vectors/{file_name}"))
94            .unwrap_or_else(|e| panic!("Failed to load test vectors from {file_name}: {e}"));
95
96        for vector in test_vectors.0 {
97            let test_name = format!("{file_name}/{}", vector.name);
98            let input = Bytes::from_hex(vector.input.clone()).unwrap_or_else(|e| {
99                panic!(
100                    "could not deserialize input {} as hex in {test_name}: {e}",
101                    &vector.input
102                )
103            });
104            let target_gas: u64 = 30_000_000;
105            let res = precompile(&input, target_gas);
106            if let Some(expected_error) = vector.expected_error {
107                assert!(res.is_err(), "expected error {expected_error} didn't happen in {test_name}, got result {res:?}");
108            } else {
109                let Some(gas) = vector.gas else {
110                    panic!("gas is missing in {test_name}");
111                };
112                let outcome = res.unwrap_or_else(|e: revm_primitives::PrecompileErrors| {
113                    panic!("precompile call failed for {test_name}: {e}")
114                });
115                assert_eq!(
116                    gas, outcome.gas_used,
117                    "expected gas: {}, actual gas: {} in {test_name}",
118                    gas, outcome.gas_used
119                );
120                let Some(expected) = vector.expected else {
121                    panic!("expected output is missing in {test_name}");
122                };
123                let expected_output = Bytes::from_hex(expected).unwrap();
124                assert_eq!(
125                    expected_output, outcome.bytes,
126                    "expected output: {expected_output}, actual output: {:?} in {test_name}",
127                    outcome.bytes
128                );
129            }
130        }
131    }
132
133    #[rstest]
134    #[case::g1_empty(0, g1_mul::BASE_GAS_FEE, 0)]
135    #[case::g1_one_item(160, g1_mul::BASE_GAS_FEE, 14400)]
136    #[case::g1_two_items(320, g1_mul::BASE_GAS_FEE, 21312)]
137    #[case::g1_ten_items(1600, g1_mul::BASE_GAS_FEE, 50760)]
138    #[case::g1_sixty_four_items(10240, g1_mul::BASE_GAS_FEE, 170496)]
139    #[case::g1_one_hundred_twenty_eight_items(20480, g1_mul::BASE_GAS_FEE, 267264)]
140    #[case::g1_one_hundred_twenty_nine_items(20640, g1_mul::BASE_GAS_FEE, 269352)]
141    #[case::g1_two_hundred_fifty_six_items(40960, g1_mul::BASE_GAS_FEE, 534528)]
142    fn test_g1_msm_required_gas(
143        #[case] input_len: usize,
144        #[case] multiplication_cost: u64,
145        #[case] expected_output: u64,
146    ) {
147        let k = input_len / g1_mul::INPUT_LENGTH;
148
149        let actual_output = msm_required_gas(k, multiplication_cost);
150
151        assert_eq!(expected_output, actual_output);
152    }
153}