Skip to content
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

feat(std_lib)!: modulus bits and modulus bytes methods #697

Merged
merged 16 commits into from
Jan 31, 2023
Merged
2 changes: 1 addition & 1 deletion crates/nargo/tests/test_data/7_function/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions crates/nargo/tests/test_data/9_conditional/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions crates/nargo/tests/test_data/modulus/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.1"

[dependencies]
3 changes: 3 additions & 0 deletions crates/nargo/tests/test_data/modulus/Prover.toml
Original file line number Diff line number Diff line change
@@ -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 = ""
27 changes: 27 additions & 0 deletions crates/nargo/tests/test_data/modulus/src/main.nr
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion crates/nargo/tests/test_data/to_le_bytes/Prover.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
x = "2040124"
return = [0x3c, 0x21, 0x1f, 0x00]
return = [0x3c, 0x21, 0x1f, 0x00]
2 changes: 1 addition & 1 deletion crates/nargo/tests/test_data/to_le_bytes/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
4 changes: 2 additions & 2 deletions crates/noirc_evaluator/src/ssa/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl std::fmt::Display for Opcode {
impl Opcode {
pub fn lookup(op_name: &str) -> Option<Opcode> {
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),
}
Expand All @@ -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",
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/noirc_evaluator/src/ssa/optim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
43 changes: 42 additions & 1 deletion crates/noirc_frontend/src/monomorphisation/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use acvm::FieldElement;
use iter_extended::{btree_map, vecmap};
use noirc_abi::Abi;
use std::collections::{BTreeMap, HashMap, VecDeque};
Expand Down Expand Up @@ -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.
Expand All @@ -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<u8>, 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,
Expand Down
26 changes: 26 additions & 0 deletions noir_stdlib/src/field.nr
Original file line number Diff line number Diff line change
@@ -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] {}
18 changes: 2 additions & 16 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,15 @@ 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
// Caution: we assume the exponent fits into 32 bits
// 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;
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/merkle.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down