pallet_revive/precompiles/builtin/
bn128.rs1use crate::{
19 precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile},
20 vm::RuntimeCosts,
21 Config,
22};
23use alloc::vec::Vec;
24use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2};
25use core::{marker::PhantomData, num::NonZero};
26use sp_core::U256;
27use sp_runtime::DispatchError;
28
29pub struct Bn128Add<T>(PhantomData<T>);
30
31impl<T: Config> PrimitivePrecompile for Bn128Add<T> {
32 type T = T;
33 const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(6).unwrap());
34 const HAS_CONTRACT_INFO: bool = false;
35
36 fn call(
37 _address: &[u8; 20],
38 input: Vec<u8>,
39 env: &mut impl Ext<T = Self::T>,
40 ) -> Result<Vec<u8>, Error> {
41 env.gas_meter_mut().charge(RuntimeCosts::Bn128Add)?;
42
43 let p1 = read_point(&input, 0)?;
44 let p2 = read_point(&input, 64)?;
45
46 let mut buf = [0u8; 64];
47 if let Some(sum) = AffineG1::from_jacobian(p1 + p2) {
48 sum.x().to_big_endian(&mut buf[0..32]).expect("0..32 is 32-byte length; qed");
50 sum.y().to_big_endian(&mut buf[32..64]).expect("32..64 is 32-byte length; qed");
51 }
52
53 Ok(buf.to_vec())
54 }
55}
56
57pub struct Bn128Mul<T>(PhantomData<T>);
58
59impl<T: Config> PrimitivePrecompile for Bn128Mul<T> {
60 type T = T;
61 const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(7).unwrap());
62 const HAS_CONTRACT_INFO: bool = false;
63
64 fn call(
65 _address: &[u8; 20],
66 input: Vec<u8>,
67 env: &mut impl Ext<T = Self::T>,
68 ) -> Result<Vec<u8>, Error> {
69 env.gas_meter_mut().charge(RuntimeCosts::Bn128Mul)?;
70
71 let p = read_point(&input, 0)?;
72 let fr = read_fr(&input, 64)?;
73
74 let mut buf = [0u8; 64];
75 if let Some(sum) = AffineG1::from_jacobian(p * fr) {
76 sum.x().to_big_endian(&mut buf[0..32]).expect("0..32 is 32-byte length; qed");
78 sum.y().to_big_endian(&mut buf[32..64]).expect("32..64 is 32-byte length; qed");
79 }
80
81 Ok(buf.to_vec())
82 }
83}
84
85pub struct Bn128Pairing<T>(PhantomData<T>);
86
87impl<T: Config> PrimitivePrecompile for Bn128Pairing<T> {
88 type T = T;
89 const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(8).unwrap());
90 const HAS_CONTRACT_INFO: bool = false;
91
92 fn call(
93 _address: &[u8; 20],
94 input: Vec<u8>,
95 env: &mut impl Ext<T = Self::T>,
96 ) -> Result<Vec<u8>, Error> {
97 if input.len() % 192 != 0 {
98 Err(DispatchError::from("invalid input length"))?;
99 }
100
101 let ret_val = if input.is_empty() {
102 env.gas_meter_mut().charge(RuntimeCosts::Bn128Pairing(0))?;
103 U256::one()
104 } else {
105 let elements = input.len() / 192;
107 env.gas_meter_mut().charge(RuntimeCosts::Bn128Pairing(elements as u32))?;
108
109 let mut vals = Vec::new();
110 for i in 0..elements {
111 let offset = i * 192;
112 let a_x = Fq::from_slice(&input[offset..offset + 32])
113 .map_err(|_| DispatchError::from("Invalid a argument x coordinate"))?;
114
115 let a_y = Fq::from_slice(&input[offset + 32..offset + 64])
116 .map_err(|_| DispatchError::from("Invalid a argument y coordinate"))?;
117
118 let b_a_y = Fq::from_slice(&input[offset + 64..offset + 96]).map_err(|_| {
119 DispatchError::from("Invalid b argument imaginary coeff x coordinate")
120 })?;
121
122 let b_a_x = Fq::from_slice(&input[offset + 96..offset + 128]).map_err(|_| {
123 DispatchError::from("Invalid b argument imaginary coeff y coordinate")
124 })?;
125
126 let b_b_y = Fq::from_slice(&input[offset + 128..offset + 160]).map_err(|_| {
127 DispatchError::from("Invalid b argument real coeff x coordinate")
128 })?;
129
130 let b_b_x = Fq::from_slice(&input[offset + 160..offset + 192]).map_err(|_| {
131 DispatchError::from("Invalid b argument real coeff y coordinate")
132 })?;
133
134 let b_a = Fq2::new(b_a_x, b_a_y);
135 let b_b = Fq2::new(b_b_x, b_b_y);
136 let b =
137 if b_a.is_zero() && b_b.is_zero() {
138 G2::zero()
139 } else {
140 G2::from(AffineG2::new(b_a, b_b).map_err(|_| {
141 DispatchError::from("Invalid b argument - not on curve")
142 })?)
143 };
144 let a =
145 if a_x.is_zero() && a_y.is_zero() {
146 G1::zero()
147 } else {
148 G1::from(AffineG1::new(a_x, a_y).map_err(|_| {
149 DispatchError::from("Invalid a argument - not on curve")
150 })?)
151 };
152 vals.push((a, b));
153 }
154
155 let mul = pairing_batch(&vals);
156
157 if mul == Gt::one() {
158 U256::one()
159 } else {
160 U256::zero()
161 }
162 };
163
164 let buf = ret_val.to_big_endian();
165 Ok(buf.to_vec())
166 }
167}
168
169fn read_point(input: &[u8], start_inx: usize) -> Result<bn::G1, DispatchError> {
170 let mut px_buf = [0u8; 32];
171 let mut py_buf = [0u8; 32];
172 read_input(input, &mut px_buf, start_inx);
173 read_input(input, &mut py_buf, start_inx + 32);
174
175 let px = Fq::from_slice(&px_buf).map_err(|_| "Invalid point x coordinate")?;
176 let py = Fq::from_slice(&py_buf).map_err(|_| "Invalid point y coordinate")?;
177
178 Ok(if px == Fq::zero() && py == Fq::zero() {
179 G1::zero()
180 } else {
181 AffineG1::new(px, py).map_err(|_| "Invalid curve point")?.into()
182 })
183}
184
185fn read_fr(input: &[u8], start_inx: usize) -> Result<bn::Fr, DispatchError> {
186 let mut buf = [0u8; 32];
187 read_input(input, &mut buf, start_inx);
188
189 let r = bn::Fr::from_slice(&buf).map_err(|_| "Invalid field element")?;
190 Ok(r)
191}
192
193fn read_input(source: &[u8], target: &mut [u8], offset: usize) {
195 if source.len() <= offset {
197 return;
198 }
199
200 let len = core::cmp::min(target.len(), source.len() - offset);
202 target[..len].copy_from_slice(&source[offset..][..len]);
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::{
209 precompiles::tests::{run_failure_test_vectors, run_test_vectors},
210 tests::Test,
211 };
212
213 #[test]
214 fn test_bn128add() {
215 run_test_vectors::<Bn128Add<Test>>(include_str!("./testdata/6-bn128add.json"));
216 run_failure_test_vectors::<Bn128Add<Test>>(include_str!(
217 "./testdata/6-bn128add-failure.json"
218 ));
219 }
220
221 #[test]
222 fn test_bn128mul() {
223 run_test_vectors::<Bn128Mul<Test>>(include_str!("./testdata/7-bn128mul.json"));
224 }
225
226 #[test]
227 fn test_bn128pairing() {
228 run_test_vectors::<Bn128Pairing<Test>>(include_str!("./testdata/8-bn128pairing.json"));
229 }
230}