Skip to content

Commit

Permalink
feat: Sync from noir (AztecProtocol#6500)
Browse files Browse the repository at this point in the history
Automated pull of development from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
feat: do not return databus returndata, keep it private.
(noir-lang/noir#5023)
fix: Fixed several vulnerabilities in U128, added some tests
(noir-lang/noir#5024)
fix: Fix no predicates for brillig with intermediate functions
(noir-lang/noir#5015)
chore: add script to print lines of code
(noir-lang/noir#4991)
END_COMMIT_OVERRIDE

Co-authored-by: TomAFrench <tom@tomfren.ch>
  • Loading branch information
AztecBot and TomAFrench authored May 17, 2024
1 parent 5c733d1 commit 26f2197
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b541e793e20fa3c991e0328ec2ff7926bdcdfd45
a5b7df12faf9d71ff24f8c5cde5e78da44558caf
12 changes: 12 additions & 0 deletions noir/noir-repo/.tokeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
docs
scripts

# aztec_macros is explicitly considered OOS for Noir audit
aztec_macros

# config files
*.toml
*.md
*.json
*.txt
*.config.mjs
21 changes: 17 additions & 4 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1741,13 +1741,16 @@ impl<'a> Context<'a> {
// will expand the array if there is one.
let return_acir_vars = self.flatten_value_list(return_values, dfg)?;
let mut warnings = Vec::new();
for acir_var in return_acir_vars {
for (acir_var, is_databus) in return_acir_vars {
if self.acir_context.is_constant(&acir_var) {
warnings.push(SsaReport::Warning(InternalWarning::ReturnConstant {
call_stack: call_stack.clone(),
}));
}
self.acir_context.return_var(acir_var)?;
if !is_databus {
// We do not return value for the data bus.
self.acir_context.return_var(acir_var)?;
}
}
Ok(warnings)
}
Expand Down Expand Up @@ -2671,12 +2674,22 @@ impl<'a> Context<'a> {
&mut self,
arguments: &[ValueId],
dfg: &DataFlowGraph,
) -> Result<Vec<AcirVar>, InternalError> {
) -> Result<Vec<(AcirVar, bool)>, InternalError> {
let mut acir_vars = Vec::with_capacity(arguments.len());
for value_id in arguments {
let is_databus = if let Some(return_databus) = self.data_bus.return_data {
dfg[*value_id] == dfg[return_databus]
} else {
false
};
let value = self.convert_value(*value_id, dfg);
acir_vars.append(
&mut self.acir_context.flatten(value)?.iter().map(|(var, _)| *var).collect(),
&mut self
.acir_context
.flatten(value)?
.iter()
.map(|(var, _)| (*var, is_databus))
.collect(),
);
}
Ok(acir_vars)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,11 @@ impl<'function> PerFunctionContext<'function> {
let function = &ssa.functions[&func_id];
// If we have not already finished the flattening pass, functions marked
// to not have predicates should be marked as entry points unless we are inlining into brillig.
let entry_point = &ssa.functions[&self.context.entry_point];
let no_predicates_is_entry_point =
self.context.no_predicates_is_entry_point
&& function.is_no_predicates()
&& !matches!(self.source_function.runtime(), RuntimeType::Brillig);
&& !matches!(entry_point.runtime(), RuntimeType::Brillig);
if function.runtime().is_entry_point() || no_predicates_is_entry_point {
self.push_instruction(*id);
} else {
Expand Down
252 changes: 237 additions & 15 deletions noir/noir-repo/noir_stdlib/src/uint128.nr
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::ops::{Add, Sub, Mul, Div, Rem, Not, BitOr, BitAnd, BitXor, Shl, Shr};
use crate::cmp::{Eq, Ord, Ordering};
use crate::println;

global pow64 : Field = 18446744073709551616; //2^64;

global pow63 : Field = 9223372036854775808; // 2^63;
struct U128 {
lo: Field,
hi: Field,
Expand All @@ -20,6 +21,13 @@ impl U128 {
U128::from_u64s_le(lo, hi)
}

pub fn zero() -> U128 {
U128 { lo: 0, hi: 0 }
}

pub fn one() -> U128 {
U128 { lo: 1, hi: 0 }
}
pub fn from_le_bytes(bytes: [u8; 16]) -> U128 {
let mut lo = 0;
let mut base = 1;
Expand Down Expand Up @@ -87,27 +95,44 @@ impl U128 {
U128 { lo: lo as Field, hi: hi as Field }
}

unconstrained fn uconstrained_check_is_upper_ascii(ascii: u8) -> bool {
((ascii >= 65) & (ascii <= 90)) // Between 'A' and 'Z'
}

fn decode_ascii(ascii: u8) -> Field {
if ascii < 58 {
ascii - 48
} else if ascii < 71 {
ascii - 55
} else {
let ascii = ascii + 32 * (U128::uconstrained_check_is_upper_ascii(ascii) as u8);
assert(ascii >= 97); // enforce >= 'a'
assert(ascii <= 102); // enforce <= 'f'
ascii - 87
} as Field
}

// TODO: Replace with a faster version.
// A circuit that uses this function can be slow to compute
// (we're doing up to 127 calls to compute the quotient)
unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) {
if self < b {
(U128::from_u64s_le(0, 0), self)
if b == U128::zero() {
// Return 0,0 to avoid eternal loop
(U128::zero(), U128::zero())
} else if self < b {
(U128::zero(), self)
} else if self == b {
(U128::one(), U128::zero())
} else {
//TODO check if this can overflow?
let (q,r) = self.unconstrained_div(b * U128::from_u64s_le(2, 0));
let (q,r) = if b.hi as u64 >= pow63 as u64 {
// The result of multiplication by 2 would overflow
(U128::zero(), self)
} else {
self.unconstrained_div(b * U128::from_u64s_le(2, 0))
};
let q_mul_2 = q * U128::from_u64s_le(2, 0);
if r < b {
(q_mul_2, r)
} else {
(q_mul_2 + U128::from_u64s_le(1, 0), r - b)
(q_mul_2 + U128::one(), r - b)
}
}
}
Expand All @@ -129,11 +154,7 @@ impl U128 {
let low = self.lo * b.lo;
let lo = low as u64 as Field;
let carry = (low - lo) / pow64;
let high = if crate::field::modulus_num_bits() as u32 > 196 {
(self.lo + self.hi) * (b.lo + b.hi) - low + carry
} else {
self.lo * b.hi + self.hi * b.lo + carry
};
let high = self.lo * b.hi + self.hi * b.lo + carry;
let hi = high as u64 as Field;
U128 { lo, hi }
}
Expand Down Expand Up @@ -294,8 +315,8 @@ impl Shr for U128 {
}
}

mod test {
use crate::uint128::{U128, pow64};
mod tests {
use crate::uint128::{U128, pow64, pow63};

#[test]
fn test_not() {
Expand All @@ -309,4 +330,205 @@ mod test {
let not_not_num = not_num.not();
assert_eq(num, not_not_num);
}

#[test]
fn test_construction() {
// Check little-endian u64 is inversed with big-endian u64 construction
let a = U128::from_u64s_le(2, 1);
let b = U128::from_u64s_be(1, 2);
assert_eq(a, b);
// Check byte construction is equivalent
let c = U128::from_le_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
let d = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908);
assert_eq(c, d);
}

#[test]
fn test_byte_decomposition() {
let a = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908);
// Get big-endian and little-endian byte decompostions
let le_bytes_a= a.to_le_bytes();
let be_bytes_a= a.to_be_bytes();

// Check equivalence
for i in 0..16 {
assert_eq(le_bytes_a[i], be_bytes_a[15 - i]);
}
// Reconstruct U128 from byte decomposition
let b= U128::from_le_bytes(le_bytes_a);
// Check that it's the same element
assert_eq(a, b);
}

#[test]
fn test_hex_constuction() {
let a = U128::from_u64s_le(0x1, 0x2);
let b = U128::from_hex("0x20000000000000001");
assert_eq(a, b);

let c= U128::from_hex("0xffffffffffffffffffffffffffffffff");
let d= U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff);
assert_eq(c, d);

let e= U128::from_hex("0x00000000000000000000000000000000");
let f= U128::from_u64s_le(0, 0);
assert_eq(e, f);
}

// Ascii decode tests

#[test]
fn test_ascii_decode_correct_range() {
// '0'..'9' range
for i in 0..10 {
let decoded= U128::decode_ascii(48 + i);
assert_eq(decoded, i as Field);
}
// 'A'..'F' range
for i in 0..6 {
let decoded = U128::decode_ascii(65 + i);
assert_eq(decoded, (i + 10) as Field);
}
// 'a'..'f' range
for i in 0..6 {
let decoded = U128::decode_ascii(97 + i);
assert_eq(decoded, (i + 10) as Field);
}
}

#[test(should_fail)]
fn test_ascii_decode_range_less_than_48_fails_0() {
crate::println(U128::decode_ascii(0));
}

#[test(should_fail)]
fn test_ascii_decode_range_less_than_48_fails_1() {
crate::println(U128::decode_ascii(47));
}

#[test(should_fail)]
fn test_ascii_decode_range_58_64_fails_0() {
let _ = U128::decode_ascii(58);
}

#[test(should_fail)]
fn test_ascii_decode_range_58_64_fails_1() {
let _ = U128::decode_ascii(64);
}

#[test(should_fail)]
fn test_ascii_decode_range_71_96_fails_0() {
let _ = U128::decode_ascii(71);
}

#[test(should_fail)]
fn test_ascii_decode_range_71_96_fails_1() {
let _ = U128::decode_ascii(96);
}

#[test(should_fail)]
fn test_ascii_decode_range_greater_than_102_fails() {
let _ = U128::decode_ascii(103);
}

#[test(should_fail)]
fn test_ascii_decode_regression() {
// This code will actually fail because of ascii_decode,
// but in the past it was possible to create a value > (1<<128)
let a = U128::from_hex("0x~fffffffffffffffffffffffffffffff");
let b:Field= a.to_integer();
let c= b.to_le_bytes(17);
assert(c[16] != 0);
}

#[test]
fn test_unconstrained_div() {
// Test the potential overflow case
let a= U128::from_u64s_le(0x0, 0xffffffffffffffff);
let b= U128::from_u64s_le(0x0, 0xfffffffffffffffe);
let c= U128::one();
let d= U128::from_u64s_le(0x0, 0x1);
let (q,r) = a.unconstrained_div(b);
assert_eq(q, c);
assert_eq(r, d);

let a = U128::from_u64s_le(2, 0);
let b = U128::one();
// Check the case where a is a multiple of b
let (c,d ) = a.unconstrained_div(b);
assert_eq((c, d), (a, U128::zero()));

// Check where b is a multiple of a
let (c,d) = b.unconstrained_div(a);
assert_eq((c, d), (U128::zero(), b));

// Dividing by zero returns 0,0
let a = U128::from_u64s_le(0x1, 0x0);
let b = U128::zero();
let (c,d)= a.unconstrained_div(b);
assert_eq((c, d), (U128::zero(), U128::zero()));

// Dividing 1<<127 by 1<<127 (special case)
let a = U128::from_u64s_le(0x0, pow63 as u64);
let b = U128::from_u64s_le(0x0, pow63 as u64);
let (c,d )= a.unconstrained_div(b);
assert_eq((c, d), (U128::one(), U128::zero()));
}

#[test]
fn integer_conversions() {
// Maximum
let start:Field = 0xffffffffffffffffffffffffffffffff;
let a = U128::from_integer(start);
let end = a.to_integer();
assert_eq(start, end);

// Minimum
let start:Field = 0x0;
let a = U128::from_integer(start);
let end = a.to_integer();
assert_eq(start, end);

// Low limb
let start:Field = 0xffffffffffffffff;
let a = U128::from_integer(start);
let end = a.to_integer();
assert_eq(start, end);

// High limb
let start:Field = 0xffffffffffffffff0000000000000000;
let a = U128::from_integer(start);
let end = a.to_integer();
assert_eq(start, end);
}
#[test]
fn test_wrapping_mul() {
// 1*0==0
assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::one()));

// 0*1==0
assert_eq(U128::zero(), U128::one().wrapping_mul(U128::zero()));

// 1*1==1
assert_eq(U128::one(), U128::one().wrapping_mul(U128::one()));

// 0 * ( 1 << 64 ) == 0
assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::from_u64s_le(0, 1)));

// ( 1 << 64 ) * 0 == 0
assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::zero()));

// 1 * ( 1 << 64 ) == 1 << 64
assert_eq(U128::from_u64s_le(0, 1), U128::from_u64s_le(0, 1).wrapping_mul(U128::one()));

// ( 1 << 64 ) * 1 == 1 << 64
assert_eq(U128::from_u64s_le(0, 1), U128::one().wrapping_mul(U128::from_u64s_le(0, 1)));

// ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64
assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::from_u64s_le(0, 1)));
// -1 * -1 == 1
assert_eq(
U128::one(), U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff).wrapping_mul(U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff))
);
}
}
Loading

0 comments on commit 26f2197

Please sign in to comment.