-
Notifications
You must be signed in to change notification settings - Fork 597
chore: add bigint solver in ACVM and add a unit test for bigints in Noir #4415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1b8e3e8
850d6ce
4b9ec0b
a5df762
159cd4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| use std::collections::HashMap; | ||
|
|
||
| use acir::{ | ||
| circuit::opcodes::FunctionInput, | ||
| native_types::{Witness, WitnessMap}, | ||
| BlackBoxFunc, FieldElement, | ||
| }; | ||
|
|
||
| use num_bigint::BigUint; | ||
|
|
||
| use crate::pwg::OpcodeResolutionError; | ||
|
|
||
| /// Resolve BigInt opcodes by storing BigInt values (and their moduli) by their ID in a HashMap: | ||
| /// - When it encounters a bigint operation opcode, it performs the operation on the stored values | ||
| /// and store the result using the provided ID. | ||
| /// - When it gets a to_bytes opcode, it simply looks up the value and resolves the output witness accordingly. | ||
| #[derive(Default)] | ||
| pub(crate) struct BigIntSolver { | ||
| bigint_id_to_value: HashMap<u32, BigUint>, | ||
| bigint_id_to_modulus: HashMap<u32, BigUint>, | ||
| } | ||
|
|
||
| impl BigIntSolver { | ||
| pub(crate) fn get_bigint( | ||
| &self, | ||
| id: u32, | ||
| func: BlackBoxFunc, | ||
| ) -> Result<BigUint, OpcodeResolutionError> { | ||
| self.bigint_id_to_value | ||
| .get(&id) | ||
| .ok_or(OpcodeResolutionError::BlackBoxFunctionFailed( | ||
| func, | ||
| format!("could not find bigint of id {id}"), | ||
| )) | ||
| .cloned() | ||
| } | ||
|
|
||
| pub(crate) fn get_modulus( | ||
| &self, | ||
| id: u32, | ||
| func: BlackBoxFunc, | ||
| ) -> Result<BigUint, OpcodeResolutionError> { | ||
| self.bigint_id_to_modulus | ||
| .get(&id) | ||
| .ok_or(OpcodeResolutionError::BlackBoxFunctionFailed( | ||
| func, | ||
| format!("could not find bigint of id {id}"), | ||
| )) | ||
| .cloned() | ||
| } | ||
| pub(crate) fn bigint_from_bytes( | ||
| &mut self, | ||
| inputs: &[FunctionInput], | ||
| modulus: &[u8], | ||
| output: u32, | ||
| initial_witness: &mut WitnessMap, | ||
| ) -> Result<(), OpcodeResolutionError> { | ||
| let bytes = inputs | ||
| .iter() | ||
| .map(|input| initial_witness.get(&input.witness).unwrap().to_u128() as u8) | ||
| .collect::<Vec<u8>>(); | ||
| let bigint = BigUint::from_bytes_le(&bytes); | ||
| self.bigint_id_to_value.insert(output, bigint); | ||
| let modulus = BigUint::from_bytes_le(modulus); | ||
| self.bigint_id_to_modulus.insert(output, modulus); | ||
| Ok(()) | ||
| } | ||
|
|
||
| pub(crate) fn bigint_to_bytes( | ||
| &self, | ||
| input: u32, | ||
| outputs: &Vec<Witness>, | ||
| initial_witness: &mut WitnessMap, | ||
| ) -> Result<(), OpcodeResolutionError> { | ||
| let bigint = self.get_bigint(input, BlackBoxFunc::BigIntToLeBytes)?; | ||
|
|
||
| let mut bytes = bigint.to_bytes_le(); | ||
| while bytes.len() < outputs.len() { | ||
| bytes.push(0); | ||
| } | ||
| bytes.iter().zip(outputs.iter()).for_each(|(byte, output)| { | ||
| initial_witness.insert(*output, FieldElement::from(*byte as u128)); | ||
| }); | ||
| Ok(()) | ||
| } | ||
|
|
||
| pub(crate) fn bigint_op( | ||
| &mut self, | ||
| lhs: u32, | ||
| rhs: u32, | ||
| output: u32, | ||
| func: BlackBoxFunc, | ||
| ) -> Result<(), OpcodeResolutionError> { | ||
| let modulus = self.get_modulus(lhs, func)?; | ||
| let lhs = self.get_bigint(lhs, func)?; | ||
| let rhs = self.get_bigint(rhs, func)?; | ||
| let mut result = match func { | ||
| BlackBoxFunc::BigIntAdd => lhs + rhs, | ||
| BlackBoxFunc::BigIntNeg => { | ||
| if lhs >= rhs { | ||
| &lhs - &rhs | ||
| } else { | ||
| &lhs + &modulus - &rhs | ||
| } | ||
| } | ||
| BlackBoxFunc::BigIntMul => lhs * rhs, | ||
| BlackBoxFunc::BigIntDiv => { | ||
| lhs * rhs.modpow(&(&modulus - BigUint::from(1_u32)), &modulus) | ||
| } //TODO ensure that modulus is prime | ||
| _ => unreachable!("ICE - bigint_op must be called for an operation"), | ||
| }; | ||
| if result > modulus { | ||
| let q = &result / &modulus; | ||
| result -= q * &modulus; | ||
| } | ||
| self.bigint_id_to_value.insert(output, result); | ||
| self.bigint_id_to_modulus.insert(output, modulus); | ||
| Ok(()) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,55 @@ | ||
| use crate::ops::{Add, Sub, Mul, Div, Rem,}; | ||
|
|
||
|
|
||
| global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, | ||
| 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; | ||
| global bn254_fr = [0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28, | ||
| 0x5D, 0x58, 0x81, 0x81, 0xB6, 0x45, 0x50, 0xB8, 0x29, 0xA0, 0x31, 0xE1, 0x72, 0x4E, 0x64, 0x30]; | ||
| global secpk1_fr = [0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, | ||
| 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; | ||
| global secpk1_fq = [0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
| 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; | ||
| global secpr1_fq = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, | ||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; | ||
| global secpr1_fr = [0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, | ||
| 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; | ||
|
|
||
|
|
||
| struct BigInt { | ||
| pointer: u32, | ||
| modulus: u32, | ||
| } | ||
|
|
||
| impl BigInt { | ||
| #[builtin(bigint_add)] | ||
| pub fn bigint_add(self, other: BigInt) -> BigInt { | ||
| fn bigint_add(self, other: BigInt) -> BigInt { | ||
| } | ||
| #[builtin(bigint_neg)] | ||
| pub fn bigint_neg(self, other: BigInt) -> BigInt { | ||
| fn bigint_neg(self, other: BigInt) -> BigInt { | ||
| } | ||
| #[builtin(bigint_mul)] | ||
| pub fn bigint_mul(self, other: BigInt) -> BigInt { | ||
| fn bigint_mul(self, other: BigInt) -> BigInt { | ||
| } | ||
| #[builtin(bigint_div)] | ||
| pub fn bigint_div(self, other: BigInt) -> BigInt { | ||
| fn bigint_div(self, other: BigInt) -> BigInt { | ||
| } | ||
| #[builtin(bigint_from_le_bytes)] | ||
| pub fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} | ||
| fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} | ||
| #[builtin(bigint_to_le_bytes)] | ||
| pub fn to_le_bytes(self) -> [u8] {} | ||
|
|
||
| pub fn bn254_fr_from_le_bytes(bytes: [u8]) -> BigInt { | ||
| BigInt::from_le_bytes(bytes, bn254_fr) | ||
|
Comment on lines
+41
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is okay to merge, but I think we should likely just have different BigInt structures so that we get type safety. Can you open up an issue for this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| pub fn bn254_fq_from_le_bytes(bytes: [u8]) -> BigInt { | ||
| BigInt::from_le_bytes(bytes, bn254_fq) | ||
| } | ||
| pub fn secpk1_fq_from_le_bytes(bytes: [u8]) -> BigInt { | ||
| BigInt::from_le_bytes(bytes, secpk1_fq) | ||
| } | ||
| pub fn secpk1_fr_from_le_bytes(bytes: [u8]) -> BigInt { | ||
| BigInt::from_le_bytes(bytes, secpk1_fr) | ||
| } | ||
| } | ||
|
|
||
| impl Add for BigInt { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [package] | ||
| name = "bigint" | ||
| type = "bin" | ||
| authors = [""] | ||
|
|
||
| [dependencies] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| x = [34,3,5,8,4] | ||
| y = [44,7,1,8,8] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| use dep::std::bigint; | ||
|
|
||
| fn main(mut x: [u8;5], y: [u8;5]) { | ||
| let a = bigint::BigInt::secpk1_fq_from_le_bytes([x[0],x[1],x[2],x[3],x[4]]); | ||
| let b = bigint::BigInt::secpk1_fq_from_le_bytes([y[0],y[1],y[2],y[3],y[4]]); | ||
|
|
||
| let a_bytes = a.to_le_bytes(); | ||
| let b_bytes = b.to_le_bytes(); | ||
| for i in 0..5 { | ||
| assert(a_bytes[i] == x[i]); | ||
| assert(b_bytes[i] == y[i]); | ||
| } | ||
|
|
||
| let d = a*b - b; | ||
| let d_bytes = d.to_le_bytes(); | ||
| let d1 = bigint::BigInt::secpk1_fq_from_le_bytes(597243850900842442924.to_le_bytes(10)); | ||
| let d1_bytes = d1.to_le_bytes(); | ||
| for i in 0..32 { | ||
| assert(d_bytes[i] == d1_bytes[i]); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.