diff --git a/crates/nargo/tests/test_data/7_function/src/main.nr b/crates/nargo/tests/test_data/7_function/src/main.nr index bc45610ff22..a57889838b7 100644 --- a/crates/nargo/tests/test_data/7_function/src/main.nr +++ b/crates/nargo/tests/test_data/7_function/src/main.nr @@ -33,7 +33,7 @@ fn test2(z: Field, t: u32 ) { fn pow(base: Field, exponent: Field) -> Field { let mut r = 1 as Field; - let b = std::to_bits(exponent, 32 as u32); + let b = std::field::to_le_bits(exponent, 32 as u32); for i in 1..33 { r = r*r; r = (b[32-i] as Field) * (r * base) + (1 - b[32-i] as Field) * r; diff --git a/crates/nargo/tests/test_data/9_conditional/src/main.nr b/crates/nargo/tests/test_data/9_conditional/src/main.nr index 8819955c758..6afc79d4be2 100644 --- a/crates/nargo/tests/test_data/9_conditional/src/main.nr +++ b/crates/nargo/tests/test_data/9_conditional/src/main.nr @@ -59,12 +59,12 @@ fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]){ } } - //Regression for to_bits() constant evaluation + //Regression for to_le_bits() constant evaluation // binary array representation of u8 1 let as_bits_hardcode_1 = [1, 0]; let mut c1 = 0; for i in 0..2 { - let mut as_bits = std::to_bits(arr[i] as Field, 2); + let mut as_bits = std::field::to_le_bits(arr[i] as Field, 2); c1 = c1 + as_bits[0] as Field; if i == 0 { diff --git a/crates/nargo/tests/test_data/modulus/Nargo.toml b/crates/nargo/tests/test_data/modulus/Nargo.toml new file mode 100644 index 00000000000..e0b467ce5da --- /dev/null +++ b/crates/nargo/tests/test_data/modulus/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo/tests/test_data/modulus/Prover.toml b/crates/nargo/tests/test_data/modulus/Prover.toml new file mode 100644 index 00000000000..d46300b8a5b --- /dev/null +++ b/crates/nargo/tests/test_data/modulus/Prover.toml @@ -0,0 +1,3 @@ +bn254_modulus_be_bytes = [48, 100, 78, 114, 225, 49, 160, 41, 184, 80, 69, 182, 129, 129, 88, 93, 40, 51, 232, 72, 121, 185, 112, 145, 67, 225, 245, 147, 240, 0, 0, 1] +bn254_modulus_be_bits = [1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] +return = "" \ No newline at end of file diff --git a/crates/nargo/tests/test_data/modulus/src/main.nr b/crates/nargo/tests/test_data/modulus/src/main.nr new file mode 100644 index 00000000000..070d934976d --- /dev/null +++ b/crates/nargo/tests/test_data/modulus/src/main.nr @@ -0,0 +1,27 @@ +use dep::std; + +fn main(bn254_modulus_be_bytes : [u8; 32], bn254_modulus_be_bits : [u1; 254]) -> pub Field { + let modulus_size = std::field::modulus_num_bits(); + // NOTE: The constraints used in this circuit will only work when testing nargo with the plonk bn254 backend + constrain modulus_size == 254; + + let modulus_be_byte_array = std::field::modulus_be_bytes(); + for i in 0..32 { + constrain modulus_be_byte_array[i] == bn254_modulus_be_bytes[i]; + } + let modulus_le_byte_array = std::field::modulus_le_bytes(); + for i in 0..32 { + constrain modulus_le_byte_array[i] == bn254_modulus_be_bytes[31-i]; + } + + let modulus_be_bits = std::field::modulus_be_bits(); + for i in 0..254 { + constrain modulus_be_bits[i] == bn254_modulus_be_bits[i]; + } + let modulus_le_bits = std::field::modulus_le_bits(); + for i in 0..254 { + constrain modulus_le_bits[i] == bn254_modulus_be_bits[253-i]; + } + + modulus_size +} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/to_le_bytes/Prover.toml b/crates/nargo/tests/test_data/to_le_bytes/Prover.toml index 54cfe16a841..f5f4f367d1c 100644 --- a/crates/nargo/tests/test_data/to_le_bytes/Prover.toml +++ b/crates/nargo/tests/test_data/to_le_bytes/Prover.toml @@ -1,2 +1,2 @@ x = "2040124" -return = [0x3c, 0x21, 0x1f, 0x00] +return = [0x3c, 0x21, 0x1f, 0x00] \ No newline at end of file diff --git a/crates/nargo/tests/test_data/to_le_bytes/src/main.nr b/crates/nargo/tests/test_data/to_le_bytes/src/main.nr index f2fbb915ddd..2fb86170230 100644 --- a/crates/nargo/tests/test_data/to_le_bytes/src/main.nr +++ b/crates/nargo/tests/test_data/to_le_bytes/src/main.nr @@ -2,7 +2,7 @@ use dep::std; fn main(x : Field) -> pub [u8; 4] { // The result of this byte array will be little-endian - let byte_array = std::to_le_bytes(x, 31); + let byte_array = std::field::to_le_bytes(x, 31); let mut first_four_bytes = [0; 4]; for i in 0..4 { first_four_bytes[i] = byte_array[i]; diff --git a/crates/noirc_evaluator/src/ssa/builtin.rs b/crates/noirc_evaluator/src/ssa/builtin.rs index b6e4eebb772..23ae36d6133 100644 --- a/crates/noirc_evaluator/src/ssa/builtin.rs +++ b/crates/noirc_evaluator/src/ssa/builtin.rs @@ -20,7 +20,7 @@ impl std::fmt::Display for Opcode { impl Opcode { pub fn lookup(op_name: &str) -> Option { match op_name { - "to_bits" => Some(Opcode::ToBits), + "to_le_bits" => Some(Opcode::ToBits), "to_radix" => Some(Opcode::ToRadix), _ => BlackBoxFunc::lookup(op_name).map(Opcode::LowLevel), } @@ -29,7 +29,7 @@ impl Opcode { pub fn name(&self) -> &str { match self { Opcode::LowLevel(op) => op.name(), - Opcode::ToBits => "to_bits", + Opcode::ToBits => "to_le_bits", Opcode::ToRadix => "to_radix", } } diff --git a/crates/noirc_evaluator/src/ssa/optim.rs b/crates/noirc_evaluator/src/ssa/optim.rs index 72c7d629c83..edad49ce32e 100644 --- a/crates/noirc_evaluator/src/ssa/optim.rs +++ b/crates/noirc_evaluator/src/ssa/optim.rs @@ -362,7 +362,7 @@ fn cse_block_with_anchor( result?; } - //cannot simplify to_bits() in the previous call because it get replaced with multiple instructions + //cannot simplify to_le_bits() in the previous call because it get replaced with multiple instructions if let Operation::Intrinsic(opcode, args) = &update2.operation { let args = args.iter().map(|arg| { NodeEval::from_id(ctx, *arg).into_const_value().map(|f| f.to_u128()) diff --git a/crates/noirc_frontend/src/monomorphisation/mod.rs b/crates/noirc_frontend/src/monomorphisation/mod.rs index 2f640866884..d20a520c4f6 100644 --- a/crates/noirc_frontend/src/monomorphisation/mod.rs +++ b/crates/noirc_frontend/src/monomorphisation/mod.rs @@ -1,3 +1,4 @@ +use acvm::FieldElement; use iter_extended::{btree_map, vecmap}; use noirc_abi::Abi; use std::collections::{BTreeMap, HashMap, VecDeque}; @@ -574,7 +575,7 @@ impl Monomorphiser { })) } - /// Try to evaluate certain builtin functions (currently only 'arraylen') + /// Try to evaluate certain builtin functions (currently only 'arraylen' and field modulus methods) /// at their callsite. /// NOTE: Evaluating at the callsite means we cannot track aliased functions. /// E.g. `let f = std::array::len; f(arr)` will fail to evaluate. @@ -595,12 +596,52 @@ impl Monomorphiser { ast::Type::Field, ))) } + Definition::Builtin(opcode) if opcode == "modulus_num_bits" => { + Some(ast::Expression::Literal(ast::Literal::Integer( + (FieldElement::max_num_bits() as u128).into(), + ast::Type::Field, + ))) + } + Definition::Builtin(opcode) if opcode == "modulus_le_bits" => { + let modulus = FieldElement::modulus(); + let bits = modulus.to_radix_le(2); + Some(self.modulus_array_literal(bits, 1)) + } + Definition::Builtin(opcode) if opcode == "modulus_be_bits" => { + let modulus = FieldElement::modulus(); + let bits = modulus.to_radix_be(2); + Some(self.modulus_array_literal(bits, 1)) + } + Definition::Builtin(opcode) if opcode == "modulus_be_bytes" => { + let modulus = FieldElement::modulus(); + let bytes = modulus.to_bytes_be(); + Some(self.modulus_array_literal(bytes, 8)) + } + Definition::Builtin(opcode) if opcode == "modulus_le_bytes" => { + let modulus = FieldElement::modulus(); + let bytes = modulus.to_bytes_le(); + Some(self.modulus_array_literal(bytes, 8)) + } _ => None, }, _ => None, } } + fn modulus_array_literal(&self, bytes: Vec, arr_elem_bits: u32) -> ast::Expression { + let bytes_as_expr = vecmap(bytes, |byte| { + ast::Expression::Literal(ast::Literal::Integer( + (byte as u128).into(), + ast::Type::Integer(crate::Signedness::Unsigned, arr_elem_bits), + )) + }); + let arr_literal = ast::ArrayLiteral { + contents: bytes_as_expr, + element_type: ast::Type::Integer(crate::Signedness::Unsigned, arr_elem_bits), + }; + ast::Expression::Literal(ast::Literal::Array(arr_literal)) + } + fn queue_function( &mut self, id: node_interner::FuncId, diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr new file mode 100644 index 00000000000..0e8a5637842 --- /dev/null +++ b/noir_stdlib/src/field.nr @@ -0,0 +1,26 @@ +#[builtin(to_le_bits)] +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] {} + +fn to_le_bytes(x : Field, byte_size: u32) -> [u8] { + to_radix(x, 256, byte_size) +} + +#[builtin(to_radix)] +//decompose _x into a _result_len vector over the _radix basis +//_radix must be less than 256 +fn to_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] {} + +#[builtin(modulus_num_bits)] +fn modulus_num_bits() -> comptime Field {} + +#[builtin(modulus_be_bits)] +fn modulus_be_bits() -> [u1] {} + +#[builtin(modulus_le_bits)] +fn modulus_le_bits() -> [u1] {} + +#[builtin(modulus_be_bytes)] +fn modulus_be_bytes() -> [u8] {} + +#[builtin(modulus_le_bytes)] +fn modulus_le_bytes() -> [u8] {} \ No newline at end of file diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index dd78ccc621c..3435dfc0f5a 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -6,21 +6,7 @@ mod ecdsa_secp256k1; mod scalar_mul; mod sha256; mod sha512; - - - #[builtin(to_bits)] -fn to_bits(_x : Field, _bit_size: u32) -> [u1] {} - -fn to_le_bytes(x : Field, byte_size: u32) -> [u8] { - to_radix(x, 256, byte_size) -} - -#[builtin(to_radix)] -//decompose _x into a _result_len vector over the _radix basis -//_radix must be less than 256 -fn to_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] {} - - +mod field; // Returns base^exponent. // ^ means to the power of and not xor @@ -28,7 +14,7 @@ fn to_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] {} // using a bigger bit size impacts negatively the performance and should be done only if the exponent does not fit in 32 bits fn pow_32(base: Field, exponent: Field) -> Field { let mut r = 1 as Field; - let b = crate::to_bits(exponent, 32); + let b = field::to_le_bits(exponent, 32); for i in 1..33 { r = r*r; diff --git a/noir_stdlib/src/merkle.nr b/noir_stdlib/src/merkle.nr index 188c7a011a5..7b6db21d755 100644 --- a/noir_stdlib/src/merkle.nr +++ b/noir_stdlib/src/merkle.nr @@ -17,7 +17,7 @@ fn check_membership_in_noir(root : Field, leaf : Field, index : Field, hash_path // Returns the root of the tree from the provided leaf and its hashpath, using pedersen hash fn compute_root_from_leaf(leaf : Field, index : Field, hash_path: [Field]) -> Field { let n = crate::array::len(hash_path); - let index_bits = crate::to_bits(index, n as u32); + let index_bits = crate::field::to_le_bits(index, n as u32); let mut current = leaf; for i in 0..n { let path_bit = index_bits[i] as bool;