referrerpolicy=no-referrer-when-downgrade

pallet_revive/precompiles/builtin/
bn128.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use 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			// point not at infinity
49			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			// point not at infinity
77			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			// (a, b_a, b_b - each 64-byte affine coordinates)
106			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
193/// Copy bytes from input to target.
194fn read_input(source: &[u8], target: &mut [u8], offset: usize) {
195	// Out of bounds, nothing to copy.
196	if source.len() <= offset {
197		return;
198	}
199
200	// Find len to copy up to target len, but not out of bounds.
201	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}