From cdfec32a7d6eb350061db2e8af6998b392ca96fe Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 28 Aug 2024 20:51:42 +0000 Subject: [PATCH 1/6] comptime keccak --- .../src/hir/comptime/interpreter/builtin.rs | 47 +++++++++++++++++-- .../interpreter/builtin/builtin_helpers.rs | 10 ++++ .../src/hir/comptime/interpreter/foreign.rs | 31 +++++++++++- noir_stdlib/src/hash/mod.nr | 4 ++ .../comptime_keccak/Nargo.toml | 7 +++ .../comptime_keccak/src/main.nr | 31 ++++++++++++ 6 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 test_programs/compile_success_empty/comptime_keccak/Nargo.toml create mode 100644 test_programs/compile_success_empty/comptime_keccak/src/main.nr diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 8aa8e92408f..8a0b520fa74 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -6,15 +6,16 @@ use std::{ use acvm::{AcirField, FieldElement}; use builtin_helpers::{ block_expression_to_value, check_argument_count, check_function_not_yet_resolved, - check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_function_def, - get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, - get_trait_impl, get_tuple, get_type, get_u32, get_unresolved_type, hir_pattern_to_tokens, - mutate_func_meta_type, parse, parse_tokens, replace_func_meta_parameters, - replace_func_meta_return_type, + check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field, + get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, + get_trait_def, get_trait_impl, get_tuple, get_type, get_u32, get_unresolved_type, + hir_pattern_to_tokens, mutate_func_meta_type, parse, parse_tokens, + replace_func_meta_parameters, replace_func_meta_return_type, }; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; +use num_bigint::BigUint; use rustc_hash::FxHashMap as HashMap; use crate::{ @@ -52,6 +53,9 @@ impl<'local, 'context> Interpreter<'local, 'context> { match name { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), + // We do not allow anything from the non-comptime context into the comptime context. + // Thus, we can just return true for `assert_constant`. + "assert_constant" => Ok(Value::Bool(true)), "as_slice" => as_slice(interner, arguments, location), "expr_as_array" => expr_as_array(arguments, return_type, location), "expr_as_assign" => expr_as_assign(arguments, return_type, location), @@ -110,6 +114,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "struct_def_as_type" => struct_def_as_type(interner, arguments, location), "struct_def_fields" => struct_def_fields(interner, arguments, location), "struct_def_generics" => struct_def_generics(interner, arguments, location), + "to_le_radix" => to_le_radix(arguments, location), // TODO "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), "trait_constraint_hash" => trait_constraint_hash(interner, arguments, location), "trait_def_as_trait_constraint" => { @@ -417,6 +422,38 @@ fn quoted_as_type( Ok(Value::Type(typ)) } +fn to_le_radix( + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (value, radix, limb_count) = check_three_arguments(arguments, location)?; + + let value = get_field(value)?; + let radix = get_u32(radix)?; + let limb_count = get_u32(limb_count)?; + + // Decompose the integer into its radix digits in little endian form. + let decomposed_integer = compute_to_radix(value, radix); + let decomposed_integer = vecmap(0..limb_count as usize, |i| match decomposed_integer.get(i) { + Some(digit) => Value::U8(*digit), + None => Value::U8(0), + }); + Ok(Value::Array( + decomposed_integer.into(), + Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + )) +} + +fn compute_to_radix(field: FieldElement, radix: u32) -> Vec { + let bit_size = u32::BITS - (radix - 1).leading_zeros(); + let radix_big = BigUint::from(radix); + assert_eq!(BigUint::from(2u128).pow(bit_size), radix_big, "ICE: Radix must be a power of 2"); + let big_integer = BigUint::from_bytes_be(&field.to_be_bytes()); + + // Decompose the integer into its radix digits in little endian form. + big_integer.to_radix_le(radix) +} + // fn as_array(self) -> Option<(Type, Type)> fn type_as_array( arguments: Vec<(Value, Location)>, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index a409731a5e4..ee9a22cfc13 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -142,6 +142,16 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { } } +pub(crate) fn get_u64((value, location): (Value, Location)) -> IResult { + match value { + Value::U64(value) => Ok(value), + value => { + let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour); + type_mismatch(value, expected, location) + } + } +} + pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult { match value { Value::Expr(expr) => Ok(expr), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index f7caf84ec42..e4dae7276a6 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,5 +1,6 @@ -use acvm::BlackBoxFunctionSolver; +use acvm::blackbox_solver::{keccakf1600, BlackBoxFunctionSolver}; use bn254_blackbox_solver::Bn254BlackBoxSolver; +use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; @@ -8,7 +9,9 @@ use crate::{ macros_api::NodeInterner, }; -use super::builtin::builtin_helpers::{check_two_arguments, get_array, get_field, get_u32}; +use super::builtin::builtin_helpers::{ + check_one_argument, check_two_arguments, get_array, get_field, get_u32, get_u64, +}; pub(super) fn call_foreign( interner: &mut NodeInterner, @@ -18,6 +21,7 @@ pub(super) fn call_foreign( ) -> IResult { match name { "poseidon2_permutation" => poseidon2_permutation(interner, arguments, location), + "keccakf1600" => comptime_keccakf1600(interner, arguments, location), _ => { let item = format!("Comptime evaluation for builtin function {name}"); Err(InterpreterError::Unimplemented { item, location }) @@ -47,3 +51,26 @@ fn poseidon2_permutation( let array = fields.into_iter().map(Value::Field).collect(); Ok(Value::Array(array, typ)) } + +fn comptime_keccakf1600( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let input = check_one_argument(arguments, location)?; + let input_location = input.1; + + let (input, typ) = get_array(interner, input)?; + + let input = try_vecmap(input, |integer| get_u64((integer, input_location)))?; + + let mut state = [0u64; 25]; + for (it, input_value) in state.iter_mut().zip(input.iter()) { + *it = *input_value; + } + let result_lanes = + keccakf1600(state).map_err(|error| InterpreterError::BlackBoxError(error, location))?; + + let array: Vector = result_lanes.into_iter().map(Value::U64).collect(); + Ok(Value::Array(array, typ)) +} diff --git a/noir_stdlib/src/hash/mod.nr b/noir_stdlib/src/hash/mod.nr index 657e1cd8309..2d46d2d3353 100644 --- a/noir_stdlib/src/hash/mod.nr +++ b/noir_stdlib/src/hash/mod.nr @@ -132,6 +132,10 @@ pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] crate::hash::keccak::keccak256(input, message_size) } +pub comptime fn keccak256_comptime(input: [u8; N], message_size: u32) -> [u8; 32] { + crate::hash::keccak::keccak256(input, message_size) +} + #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} diff --git a/test_programs/compile_success_empty/comptime_keccak/Nargo.toml b/test_programs/compile_success_empty/comptime_keccak/Nargo.toml new file mode 100644 index 00000000000..47c8654804d --- /dev/null +++ b/test_programs/compile_success_empty/comptime_keccak/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_keccak" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/comptime_keccak/src/main.nr b/test_programs/compile_success_empty/comptime_keccak/src/main.nr new file mode 100644 index 00000000000..3cde32b6ba9 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_keccak/src/main.nr @@ -0,0 +1,31 @@ +// Tests a very simple program. +// +// The features being tested is keccak256 in brillig +fn main() { + comptime + { + let x = 0xbd; + let result = [ + 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, 0x4b, 0x3b, 0x1a, 0xbf + ]; + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = keccak256([x as u8], 1); + assert(digest == result); + //#1399: variable message size + let message_size = 4; + let hash_a = keccak256([1, 2, 3, 4], message_size); + let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); + } +} + +comptime fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { + std::hash::keccak256(data, msg_len) +} From bd29d4528da17c9ccc3ac256a0ec574dcda24770 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 28 Aug 2024 20:55:43 +0000 Subject: [PATCH 2/6] remove keccak mcomptime method from stdlib --- noir_stdlib/src/hash/mod.nr | 4 ---- 1 file changed, 4 deletions(-) diff --git a/noir_stdlib/src/hash/mod.nr b/noir_stdlib/src/hash/mod.nr index 2d46d2d3353..657e1cd8309 100644 --- a/noir_stdlib/src/hash/mod.nr +++ b/noir_stdlib/src/hash/mod.nr @@ -132,10 +132,6 @@ pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] crate::hash::keccak::keccak256(input, message_size) } -pub comptime fn keccak256_comptime(input: [u8; N], message_size: u32) -> [u8; 32] { - crate::hash::keccak::keccak256(input, message_size) -} - #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} From bd62f8b888c8631a3c7b448a94dc39211990d57b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 28 Aug 2024 20:58:01 +0000 Subject: [PATCH 3/6] Cargo fmt --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 8a0b520fa74..79b5d049800 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -422,10 +422,7 @@ fn quoted_as_type( Ok(Value::Type(typ)) } -fn to_le_radix( - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { +fn to_le_radix(arguments: Vec<(Value, Location)>, location: Location) -> IResult { let (value, radix, limb_count) = check_three_arguments(arguments, location)?; let value = get_field(value)?; From 45a63a0e4799755f7e3eb214c31ddfc8b7ce9aa8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 28 Aug 2024 21:09:18 +0000 Subject: [PATCH 4/6] rename comptime_keccakf1600 to keccakf1600 and delete todo --- .../src/hir/comptime/interpreter/builtin.rs | 2 +- .../src/hir/comptime/interpreter/foreign.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 79b5d049800..a5d69a40a08 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -114,7 +114,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "struct_def_as_type" => struct_def_as_type(interner, arguments, location), "struct_def_fields" => struct_def_fields(interner, arguments, location), "struct_def_generics" => struct_def_generics(interner, arguments, location), - "to_le_radix" => to_le_radix(arguments, location), // TODO + "to_le_radix" => to_le_radix(arguments, location), "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), "trait_constraint_hash" => trait_constraint_hash(interner, arguments, location), "trait_def_as_trait_constraint" => { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index e4dae7276a6..5ae60bb4d00 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,4 +1,4 @@ -use acvm::blackbox_solver::{keccakf1600, BlackBoxFunctionSolver}; +use acvm::blackbox_solver::BlackBoxFunctionSolver; use bn254_blackbox_solver::Bn254BlackBoxSolver; use im::Vector; use iter_extended::try_vecmap; @@ -21,7 +21,7 @@ pub(super) fn call_foreign( ) -> IResult { match name { "poseidon2_permutation" => poseidon2_permutation(interner, arguments, location), - "keccakf1600" => comptime_keccakf1600(interner, arguments, location), + "keccakf1600" => keccakf1600(interner, arguments, location), _ => { let item = format!("Comptime evaluation for builtin function {name}"); Err(InterpreterError::Unimplemented { item, location }) @@ -52,7 +52,7 @@ fn poseidon2_permutation( Ok(Value::Array(array, typ)) } -fn comptime_keccakf1600( +fn keccakf1600( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, @@ -68,8 +68,8 @@ fn comptime_keccakf1600( for (it, input_value) in state.iter_mut().zip(input.iter()) { *it = *input_value; } - let result_lanes = - keccakf1600(state).map_err(|error| InterpreterError::BlackBoxError(error, location))?; + let result_lanes = acvm::blackbox_solver::keccakf1600(state) + .map_err(|error| InterpreterError::BlackBoxError(error, location))?; let array: Vector = result_lanes.into_iter().map(Value::U64).collect(); Ok(Value::Array(array, typ)) From 178fd1467e2cd04d8b021256e6f85d9fff3a8011 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 28 Aug 2024 17:09:33 -0400 Subject: [PATCH 5/6] Update compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index a5d69a40a08..4fa75d9b5ce 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -53,8 +53,6 @@ impl<'local, 'context> Interpreter<'local, 'context> { match name { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), - // We do not allow anything from the non-comptime context into the comptime context. - // Thus, we can just return true for `assert_constant`. "assert_constant" => Ok(Value::Bool(true)), "as_slice" => as_slice(interner, arguments, location), "expr_as_array" => expr_as_array(arguments, return_type, location), From e99e9593c410aad5ad079af76c4bd3c615e35f56 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 28 Aug 2024 21:52:21 +0000 Subject: [PATCH 6/6] clippy after merge --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 38bc944a5bf..852733b6ca8 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -9,8 +9,8 @@ use builtin_helpers::{ check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field, get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_u32, get_unresolved_type, - hir_pattern_to_tokens, mutate_func_meta_type, parse, parse_tokens, - replace_func_meta_parameters, replace_func_meta_return_type, + hir_pattern_to_tokens, mutate_func_meta_type, parse, replace_func_meta_parameters, + replace_func_meta_return_type, }; use chumsky::{prelude::choice, Parser}; use im::Vector;