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
17pub 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 #[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}