revm_precompile/
bn128.rs

1use crate::{
2    utilities::{bool_to_bytes32, right_pad},
3    Address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress,
4};
5use std::vec::Vec;
6
7cfg_if::cfg_if! {
8    if #[cfg(feature = "bn")]{
9        mod substrate;
10        use substrate::{
11            encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point,
12            read_scalar,
13        };
14    } else {
15        mod arkworks;
16        use arkworks::{
17            encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point,
18            read_scalar,
19        };
20    }
21}
22
23pub mod add {
24    use super::*;
25
26    const ADDRESS: Address = crate::u64_to_address(6);
27
28    pub const ISTANBUL_ADD_GAS_COST: u64 = 150;
29    pub const ISTANBUL: PrecompileWithAddress =
30        PrecompileWithAddress(ADDRESS, |input, gas_limit| {
31            run_add(input, ISTANBUL_ADD_GAS_COST, gas_limit)
32        });
33
34    pub const BYZANTIUM_ADD_GAS_COST: u64 = 500;
35    pub const BYZANTIUM: PrecompileWithAddress =
36        PrecompileWithAddress(ADDRESS, |input, gas_limit| {
37            run_add(input, BYZANTIUM_ADD_GAS_COST, gas_limit)
38        });
39}
40
41pub mod mul {
42    use super::*;
43
44    const ADDRESS: Address = crate::u64_to_address(7);
45
46    pub const ISTANBUL_MUL_GAS_COST: u64 = 6_000;
47    pub const ISTANBUL: PrecompileWithAddress =
48        PrecompileWithAddress(ADDRESS, |input, gas_limit| {
49            run_mul(input, ISTANBUL_MUL_GAS_COST, gas_limit)
50        });
51
52    pub const BYZANTIUM_MUL_GAS_COST: u64 = 40_000;
53    pub const BYZANTIUM: PrecompileWithAddress =
54        PrecompileWithAddress(ADDRESS, |input, gas_limit| {
55            run_mul(input, BYZANTIUM_MUL_GAS_COST, gas_limit)
56        });
57}
58
59pub mod pair {
60    use super::*;
61
62    pub const ADDRESS: Address = crate::u64_to_address(8);
63
64    pub const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
65    pub const ISTANBUL_PAIR_BASE: u64 = 45_000;
66    pub const ISTANBUL: PrecompileWithAddress =
67        PrecompileWithAddress(ADDRESS, |input, gas_limit| {
68            run_pair(
69                input,
70                ISTANBUL_PAIR_PER_POINT,
71                ISTANBUL_PAIR_BASE,
72                gas_limit,
73            )
74        });
75
76    pub const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000;
77    pub const BYZANTIUM_PAIR_BASE: u64 = 100_000;
78    pub const BYZANTIUM: PrecompileWithAddress =
79        PrecompileWithAddress(ADDRESS, |input, gas_limit| {
80            run_pair(
81                input,
82                BYZANTIUM_PAIR_PER_POINT,
83                BYZANTIUM_PAIR_BASE,
84                gas_limit,
85            )
86        });
87}
88
89/// FQ_LEN specifies the number of bytes needed to represent an
90/// Fq element. This is an element in the base field of BN254.
91///
92/// Note: The base field is used to define G1 and G2 elements.
93const FQ_LEN: usize = 32;
94
95/// SCALAR_LEN specifies the number of bytes needed to represent an Fr element.
96/// This is an element in the scalar field of BN254.
97const SCALAR_LEN: usize = 32;
98
99/// FQ2_LEN specifies the number of bytes needed to represent an
100/// Fq^2 element.
101///
102/// Note: This is the quadratic extension of Fq, and by definition
103/// means we need 2 Fq elements.
104const FQ2_LEN: usize = 2 * FQ_LEN;
105
106/// G1_LEN specifies the number of bytes needed to represent a G1 element.
107///
108/// Note: A G1 element contains 2 Fq elements.
109const G1_LEN: usize = 2 * FQ_LEN;
110/// G2_LEN specifies the number of bytes needed to represent a G2 element.
111///
112/// Note: A G2 element contains 2 Fq^2 elements.
113const G2_LEN: usize = 2 * FQ2_LEN;
114
115/// Input length for the add operation.
116/// `ADD` takes two uncompressed G1 points (64 bytes each).
117pub const ADD_INPUT_LEN: usize = 2 * G1_LEN;
118
119/// Input length for the multiplication operation.
120/// `MUL` takes an uncompressed G1 point (64 bytes) and scalar (32 bytes).
121pub const MUL_INPUT_LEN: usize = G1_LEN + SCALAR_LEN;
122
123/// Pair element length.
124/// `PAIR` elements are composed of an uncompressed G1 point (64 bytes) and an uncompressed G2 point
125/// (128 bytes).
126pub const PAIR_ELEMENT_LEN: usize = G1_LEN + G2_LEN;
127
128pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult {
129    if gas_cost > gas_limit {
130        return Err(PrecompileError::OutOfGas);
131    }
132
133    let input = right_pad::<ADD_INPUT_LEN>(input);
134
135    let p1 = read_g1_point(&input[..G1_LEN])?;
136    let p2 = read_g1_point(&input[G1_LEN..])?;
137    let result = g1_point_add(p1, p2);
138
139    let output = encode_g1_point(result);
140
141    Ok(PrecompileOutput::new(gas_cost, output.into()))
142}
143
144pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult {
145    if gas_cost > gas_limit {
146        return Err(PrecompileError::OutOfGas);
147    }
148
149    let input = right_pad::<MUL_INPUT_LEN>(input);
150
151    let p = read_g1_point(&input[..G1_LEN])?;
152
153    let scalar = read_scalar(&input[G1_LEN..G1_LEN + SCALAR_LEN]);
154    let result = g1_point_mul(p, scalar);
155
156    let output = encode_g1_point(result);
157
158    Ok(PrecompileOutput::new(gas_cost, output.into()))
159}
160
161pub fn run_pair(
162    input: &[u8],
163    pair_per_point_cost: u64,
164    pair_base_cost: u64,
165    gas_limit: u64,
166) -> PrecompileResult {
167    let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost;
168    if gas_used > gas_limit {
169        return Err(PrecompileError::OutOfGas);
170    }
171
172    if input.len() % PAIR_ELEMENT_LEN != 0 {
173        return Err(PrecompileError::Bn128PairLength);
174    }
175
176    let elements = input.len() / PAIR_ELEMENT_LEN;
177
178    let mut points = Vec::with_capacity(elements);
179
180    for idx in 0..elements {
181        // Offset to the start of the pairing element at index `idx` in the byte slice
182        let start = idx * PAIR_ELEMENT_LEN;
183        let g1_start = start;
184        // Offset to the start of the G2 element in the pairing element
185        // This is where G1 ends.
186        let g2_start = start + G1_LEN;
187
188        let encoded_g1_element = &input[g1_start..g2_start];
189        let encoded_g2_element = &input[g2_start..g2_start + G2_LEN];
190
191        // If either the G1 or G2 element is the encoded representation
192        // of the point at infinity, then these two points are no-ops
193        // in the pairing computation.
194        //
195        // Note: we do not skip the validation of these two elements even if
196        // one of them is the point at infinity because we could have G1 be
197        // the point at infinity and G2 be an invalid element or vice versa.
198        // In that case, the precompile should error because one of the elements
199        // was invalid.
200        let g1_is_zero = encoded_g1_element.iter().all(|i| *i == 0);
201        let g2_is_zero = encoded_g2_element.iter().all(|i| *i == 0);
202
203        // Get G1 and G2 points from the input
204        let a = read_g1_point(encoded_g1_element)?;
205        let b = read_g2_point(encoded_g2_element)?;
206
207        if !g1_is_zero && !g2_is_zero {
208            points.push((a, b));
209        }
210    }
211
212    let success = pairing_check(&points);
213
214    Ok(PrecompileOutput::new(gas_used, bool_to_bytes32(success)))
215}
216
217#[cfg(test)]
218mod tests {
219    use crate::{
220        bn128::{
221            add::BYZANTIUM_ADD_GAS_COST,
222            mul::BYZANTIUM_MUL_GAS_COST,
223            pair::{BYZANTIUM_PAIR_BASE, BYZANTIUM_PAIR_PER_POINT},
224        },
225        PrecompileError,
226    };
227    use primitives::hex;
228
229    use super::*;
230
231    #[test]
232    fn test_alt_bn128_add() {
233        let input = hex::decode(
234            "\
235             18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\
236             063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\
237             07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\
238             06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7",
239        )
240        .unwrap();
241        let expected = hex::decode(
242            "\
243            2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\
244            301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915",
245        )
246        .unwrap();
247
248        let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
249        assert_eq!(outcome.bytes, expected);
250
251        // Zero sum test
252        let input = hex::decode(
253            "\
254            0000000000000000000000000000000000000000000000000000000000000000\
255            0000000000000000000000000000000000000000000000000000000000000000\
256            0000000000000000000000000000000000000000000000000000000000000000\
257            0000000000000000000000000000000000000000000000000000000000000000",
258        )
259        .unwrap();
260        let expected = hex::decode(
261            "\
262            0000000000000000000000000000000000000000000000000000000000000000\
263            0000000000000000000000000000000000000000000000000000000000000000",
264        )
265        .unwrap();
266
267        let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
268        assert_eq!(outcome.bytes, expected);
269
270        // Out of gas test
271        let input = hex::decode(
272            "\
273            0000000000000000000000000000000000000000000000000000000000000000\
274            0000000000000000000000000000000000000000000000000000000000000000\
275            0000000000000000000000000000000000000000000000000000000000000000\
276            0000000000000000000000000000000000000000000000000000000000000000",
277        )
278        .unwrap();
279
280        let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 499);
281
282        assert!(matches!(res, Err(PrecompileError::OutOfGas)));
283
284        // No input test
285        let input = [0u8; 0];
286        let expected = hex::decode(
287            "\
288            0000000000000000000000000000000000000000000000000000000000000000\
289            0000000000000000000000000000000000000000000000000000000000000000",
290        )
291        .unwrap();
292
293        let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
294        assert_eq!(outcome.bytes, expected);
295
296        // Point not on curve fail
297        let input = hex::decode(
298            "\
299            1111111111111111111111111111111111111111111111111111111111111111\
300            1111111111111111111111111111111111111111111111111111111111111111\
301            1111111111111111111111111111111111111111111111111111111111111111\
302            1111111111111111111111111111111111111111111111111111111111111111",
303        )
304        .unwrap();
305
306        let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500);
307        assert!(matches!(
308            res,
309            Err(PrecompileError::Bn128AffineGFailedToCreate)
310        ));
311    }
312
313    #[test]
314    fn test_alt_bn128_mul() {
315        let input = hex::decode(
316            "\
317            2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\
318            21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\
319            00000000000000000000000000000000000000000000000011138ce750fa15c2",
320        )
321        .unwrap();
322        let expected = hex::decode(
323            "\
324            070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\
325            031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc",
326        )
327        .unwrap();
328
329        let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
330        assert_eq!(outcome.bytes, expected);
331
332        // Out of gas test
333        let input = hex::decode(
334            "\
335            0000000000000000000000000000000000000000000000000000000000000000\
336            0000000000000000000000000000000000000000000000000000000000000000\
337            0200000000000000000000000000000000000000000000000000000000000000",
338        )
339        .unwrap();
340
341        let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 39_999);
342        assert!(matches!(res, Err(PrecompileError::OutOfGas)));
343
344        // Zero multiplication test
345        let input = hex::decode(
346            "\
347            0000000000000000000000000000000000000000000000000000000000000000\
348            0000000000000000000000000000000000000000000000000000000000000000\
349            0200000000000000000000000000000000000000000000000000000000000000",
350        )
351        .unwrap();
352        let expected = hex::decode(
353            "\
354            0000000000000000000000000000000000000000000000000000000000000000\
355            0000000000000000000000000000000000000000000000000000000000000000",
356        )
357        .unwrap();
358
359        let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
360        assert_eq!(outcome.bytes, expected);
361
362        // No input test
363        let input = [0u8; 0];
364        let expected = hex::decode(
365            "\
366            0000000000000000000000000000000000000000000000000000000000000000\
367            0000000000000000000000000000000000000000000000000000000000000000",
368        )
369        .unwrap();
370
371        let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
372        assert_eq!(outcome.bytes, expected);
373
374        // Point not on curve fail
375        let input = hex::decode(
376            "\
377            1111111111111111111111111111111111111111111111111111111111111111\
378            1111111111111111111111111111111111111111111111111111111111111111\
379            0f00000000000000000000000000000000000000000000000000000000000000",
380        )
381        .unwrap();
382
383        let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000);
384        assert!(matches!(
385            res,
386            Err(PrecompileError::Bn128AffineGFailedToCreate)
387        ));
388    }
389
390    #[test]
391    fn test_alt_bn128_pair() {
392        let input = hex::decode(
393            "\
394            1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
395            3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
396            209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
397            04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
398            2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
399            120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
400            111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
401            2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
402            198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
403            1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
404            090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
405            12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
406        )
407        .unwrap();
408        let expected =
409            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
410                .unwrap();
411
412        let outcome = run_pair(
413            &input,
414            BYZANTIUM_PAIR_PER_POINT,
415            BYZANTIUM_PAIR_BASE,
416            260_000,
417        )
418        .unwrap();
419        assert_eq!(outcome.bytes, expected);
420
421        // Out of gas test
422        let input = hex::decode(
423            "\
424            1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
425            3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
426            209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
427            04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
428            2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
429            120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
430            111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
431            2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
432            198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
433            1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
434            090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
435            12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
436        )
437        .unwrap();
438
439        let res = run_pair(
440            &input,
441            BYZANTIUM_PAIR_PER_POINT,
442            BYZANTIUM_PAIR_BASE,
443            259_999,
444        );
445        assert!(matches!(res, Err(PrecompileError::OutOfGas)));
446
447        // No input test
448        let input = [0u8; 0];
449        let expected =
450            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
451                .unwrap();
452
453        let outcome = run_pair(
454            &input,
455            BYZANTIUM_PAIR_PER_POINT,
456            BYZANTIUM_PAIR_BASE,
457            260_000,
458        )
459        .unwrap();
460        assert_eq!(outcome.bytes, expected);
461
462        // Point not on curve fail
463        let input = hex::decode(
464            "\
465            1111111111111111111111111111111111111111111111111111111111111111\
466            1111111111111111111111111111111111111111111111111111111111111111\
467            1111111111111111111111111111111111111111111111111111111111111111\
468            1111111111111111111111111111111111111111111111111111111111111111\
469            1111111111111111111111111111111111111111111111111111111111111111\
470            1111111111111111111111111111111111111111111111111111111111111111",
471        )
472        .unwrap();
473
474        let res = run_pair(
475            &input,
476            BYZANTIUM_PAIR_PER_POINT,
477            BYZANTIUM_PAIR_BASE,
478            260_000,
479        );
480        assert!(matches!(
481            res,
482            Err(PrecompileError::Bn128AffineGFailedToCreate)
483        ));
484
485        // Invalid input length
486        let input = hex::decode(
487            "\
488            1111111111111111111111111111111111111111111111111111111111111111\
489            1111111111111111111111111111111111111111111111111111111111111111\
490            111111111111111111111111111111\
491        ",
492        )
493        .unwrap();
494
495        let res = run_pair(
496            &input,
497            BYZANTIUM_PAIR_PER_POINT,
498            BYZANTIUM_PAIR_BASE,
499            260_000,
500        );
501        assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
502    }
503}