From 8d039c6808bd173f4257bb6eb61c8279d3c301b7 Mon Sep 17 00:00:00 2001 From: Savio <72797635+Savio-Sou@users.noreply.github.com> Date: Fri, 7 Jun 2024 02:09:58 +0900 Subject: [PATCH 01/57] chore(docs): Update docs homepage (#5198) # Description ## Problem\* Description of Noir on the page is outdated and not fully accurate: - Implies support for SNARK proving systems only - Defines ZKP as Zero-Knowledge _Programs_ ## Summary\* This PR updates the descriptions accordingly. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- docs/docs/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index 75086ddcdde..a6bd306f91d 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -24,9 +24,9 @@ import TabItem from '@theme/TabItem'; Noir Logo -Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. +Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. -ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). +ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). ## What's new about Noir? From 42e0eb9145eb375ff42801793843c1333c2ce36c Mon Sep 17 00:00:00 2001 From: Savio <72797635+Savio-Sou@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:17:47 +0900 Subject: [PATCH 02/57] chore(docs): Supplement Noir Debugger's dependency versions (#5199) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Dependency versions of Noir / Nargo / vscode-noir for using the Noir Debugger are currently listed as "0.xx" in the docs. ## Summary\* This PR updates to docs to specify the actual versions required: - Noir & Nargo ≥0.28.0 - Noir's VS Code extension ≥0.0.11 ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- docs/docs/tooling/debugger.md | 5 ++--- docs/versioned_docs/version-v0.28.0/tooling/debugger.md | 5 ++--- docs/versioned_docs/version-v0.29.0/tooling/debugger.md | 5 ++--- docs/versioned_docs/version-v0.30.0/tooling/debugger.md | 5 ++--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/docs/tooling/debugger.md b/docs/docs/tooling/debugger.md index 184c436068f..7c158d949d1 100644 --- a/docs/docs/tooling/debugger.md +++ b/docs/docs/tooling/debugger.md @@ -14,9 +14,8 @@ There are currently two ways of debugging Noir programs: In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: -- Noir 0.xx -- Nargo 0.xx -- vscode-noir 0.xx +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 :::info At the moment, the debugger supports debugging binary projects, but not contracts. diff --git a/docs/versioned_docs/version-v0.28.0/tooling/debugger.md b/docs/versioned_docs/version-v0.28.0/tooling/debugger.md index 184c436068f..7c158d949d1 100644 --- a/docs/versioned_docs/version-v0.28.0/tooling/debugger.md +++ b/docs/versioned_docs/version-v0.28.0/tooling/debugger.md @@ -14,9 +14,8 @@ There are currently two ways of debugging Noir programs: In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: -- Noir 0.xx -- Nargo 0.xx -- vscode-noir 0.xx +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 :::info At the moment, the debugger supports debugging binary projects, but not contracts. diff --git a/docs/versioned_docs/version-v0.29.0/tooling/debugger.md b/docs/versioned_docs/version-v0.29.0/tooling/debugger.md index 184c436068f..7c158d949d1 100644 --- a/docs/versioned_docs/version-v0.29.0/tooling/debugger.md +++ b/docs/versioned_docs/version-v0.29.0/tooling/debugger.md @@ -14,9 +14,8 @@ There are currently two ways of debugging Noir programs: In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: -- Noir 0.xx -- Nargo 0.xx -- vscode-noir 0.xx +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 :::info At the moment, the debugger supports debugging binary projects, but not contracts. diff --git a/docs/versioned_docs/version-v0.30.0/tooling/debugger.md b/docs/versioned_docs/version-v0.30.0/tooling/debugger.md index 184c436068f..7c158d949d1 100644 --- a/docs/versioned_docs/version-v0.30.0/tooling/debugger.md +++ b/docs/versioned_docs/version-v0.30.0/tooling/debugger.md @@ -14,9 +14,8 @@ There are currently two ways of debugging Noir programs: In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: -- Noir 0.xx -- Nargo 0.xx -- vscode-noir 0.xx +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 :::info At the moment, the debugger supports debugging binary projects, but not contracts. From 7f08343dfcafddfcec1b238746a69273ae4f4e2b Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 7 Jun 2024 15:17:12 +0100 Subject: [PATCH 03/57] feat: Implement println in the comptime interpreter (#5197) # Description ## Problem\* Resolves ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/ast/expression.rs | 4 +- .../src/elaborator/expressions.rs | 2 +- .../noirc_frontend/src/hir/comptime/errors.rs | 2 +- .../src/hir/comptime/interpreter.rs | 68 +++++++++++++++++-- .../noirc_frontend/src/hir/comptime/value.rs | 52 +++++++++++++- .../src/hir/resolution/resolver.rs | 4 +- compiler/noirc_frontend/src/lexer/token.rs | 11 ++- .../src/monomorphization/mod.rs | 23 +++---- compiler/noirc_frontend/src/parser/parser.rs | 4 +- .../comptime_println/Nargo.toml | 7 ++ .../comptime_println/src/main.nr | 7 ++ tooling/nargo_fmt/src/rewrite/expr.rs | 4 +- 12 files changed, 154 insertions(+), 34 deletions(-) create mode 100644 test_programs/execution_success/comptime_println/Nargo.toml create mode 100644 test_programs/execution_success/comptime_println/src/main.nr diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 749e41d9c1c..50836add8de 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -34,7 +34,7 @@ pub enum ExpressionKind { Lambda(Box), Parenthesized(Box), Quote(BlockExpression), - Comptime(BlockExpression), + Comptime(BlockExpression, Span), // This variant is only emitted when inlining the result of comptime // code. It is used to translate function values back into the AST while @@ -536,7 +536,7 @@ impl Display for ExpressionKind { Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), Quote(block) => write!(f, "quote {block}"), - Comptime(block) => write!(f, "comptime {block}"), + Comptime(block, _) => write!(f, "comptime {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), } diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 5eee7ee6c7c..a922f552c4b 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -59,7 +59,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), ExpressionKind::Quote(quote) => self.elaborate_quote(quote), - ExpressionKind::Comptime(comptime) => { + ExpressionKind::Comptime(comptime, _) => { return self.elaborate_comptime_block(comptime, expr.span) } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index df4bc941f66..05962420f8a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -36,7 +36,7 @@ pub enum InterpreterError { CannotInlineMacro { value: Value, location: Location }, UnquoteFoundDuringEvaluation { location: Location }, - Unimplemented { item: &'static str, location: Location }, + Unimplemented { item: String, location: Location }, // Perhaps this should be unreachable! due to type checking also preventing this error? // Currently it and the Continue variant are the only interpreter errors without a Location field diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index e1e19ad653c..dd436144969 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -59,11 +59,6 @@ impl<'a> Interpreter<'a> { let previous_state = self.enter_function(); let meta = self.interner.function_meta(&function); - if meta.kind != FunctionKind::Normal { - let item = "Evaluation for builtin functions"; - return Err(InterpreterError::Unimplemented { item, location }); - } - if meta.parameters.len() != arguments.len() { return Err(InterpreterError::ArgumentCountMismatch { expected: meta.parameters.len(), @@ -72,6 +67,10 @@ impl<'a> Interpreter<'a> { }); } + if meta.kind != FunctionKind::Normal { + return self.call_builtin(function, arguments, location); + } + let parameters = meta.parameters.0.clone(); for ((parameter, typ, _), (argument, arg_location)) in parameters.iter().zip(arguments) { self.define_pattern(parameter, typ, argument, arg_location)?; @@ -84,6 +83,35 @@ impl<'a> Interpreter<'a> { Ok(result) } + fn call_builtin( + &mut self, + function: FuncId, + arguments: Vec<(Value, Location)>, + location: Location, + ) -> IResult { + let attributes = self.interner.function_attributes(&function); + let func_attrs = attributes.function.as_ref() + .expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); + + if let Some(builtin) = func_attrs.builtin() { + let item = format!("Evaluation for builtin functions like {builtin}"); + Err(InterpreterError::Unimplemented { item, location }) + } else if let Some(foreign) = func_attrs.foreign() { + let item = format!("Evaluation for foreign functions like {foreign}"); + Err(InterpreterError::Unimplemented { item, location }) + } else if let Some(oracle) = func_attrs.oracle() { + if oracle == "print" { + self.print_oracle(arguments) + } else { + let item = format!("Evaluation for oracle functions like {oracle}"); + Err(InterpreterError::Unimplemented { item, location }) + } + } else { + let name = self.interner.function_name(&function); + unreachable!("Non-builtin, lowlevel or oracle builtin fn '{name}'") + } + } + fn call_closure( &mut self, closure: HirLambda, @@ -219,7 +247,8 @@ impl<'a> Interpreter<'a> { argument: Value, location: Location, ) -> IResult<()> { - self.type_check(typ, &argument, location)?; + // Temporarily disabled since this fails on generic types + // self.type_check(typ, &argument, location)?; self.current_scope_mut().insert(id, argument); Ok(()) } @@ -360,7 +389,11 @@ impl<'a> Interpreter<'a> { self.evaluate_integer(value, is_negative, id) } HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), - HirLiteral::FmtStr(_, _) => todo!("Evaluate format strings"), + HirLiteral::FmtStr(_, _) => { + let item = "format strings in a comptime context".into(); + let location = self.interner.expr_location(&id); + Err(InterpreterError::Unimplemented { item, location }) + } HirLiteral::Array(array) => self.evaluate_array(array, id), HirLiteral::Slice(array) => self.evaluate_slice(array, id), } @@ -454,6 +487,14 @@ impl<'a> Interpreter<'a> { Ok(Value::I64(value)) } } + } else if let Type::TypeVariable(variable, TypeVariableKind::IntegerOrField) = &typ { + Ok(Value::Field(value)) + } else if let Type::TypeVariable(variable, TypeVariableKind::Integer) = &typ { + let value: u64 = value + .try_to_u64() + .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; + let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + Ok(Value::U64(value)) } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) } @@ -1269,4 +1310,17 @@ impl<'a> Interpreter<'a> { pub(super) fn evaluate_comptime(&mut self, statement: StmtId) -> IResult { self.evaluate_statement(statement) } + + fn print_oracle(&self, arguments: Vec<(Value, Location)>) -> Result { + assert_eq!(arguments.len(), 2); + + let print_newline = arguments[0].0 == Value::Bool(true); + if print_newline { + println!("{}", arguments[1].0); + } else { + print!("{}", arguments[1].0); + } + + Ok(Value::Unit) + } } diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 43d461ad043..11bbbc7484d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, rc::Rc}; +use std::{borrow::Cow, fmt::Display, rc::Rc}; use acvm::{AcirField, FieldElement}; use im::Vector; @@ -135,7 +135,7 @@ impl Value { } Value::Closure(_lambda, _env, _typ) => { // TODO: How should a closure's environment be inlined? - let item = "Returning closures from a comptime fn"; + let item = "Returning closures from a comptime fn".into(); return Err(InterpreterError::Unimplemented { item, location }); } Value::Tuple(fields) => { @@ -235,7 +235,7 @@ impl Value { } Value::Closure(_lambda, _env, _typ) => { // TODO: How should a closure's environment be inlined? - let item = "Returning closures from a comptime fn"; + let item = "Returning closures from a comptime fn".into(); return Err(InterpreterError::Unimplemented { item, location }); } Value::Tuple(fields) => { @@ -306,3 +306,49 @@ impl Value { fn unwrap_rc(rc: Rc) -> T { Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()) } + +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Unit => write!(f, "()"), + Value::Bool(value) => { + let msg = if *value { "true" } else { "false" }; + write!(f, "{msg}") + } + Value::Field(value) => write!(f, "{value}"), + Value::I8(value) => write!(f, "{value}"), + Value::I16(value) => write!(f, "{value}"), + Value::I32(value) => write!(f, "{value}"), + Value::I64(value) => write!(f, "{value}"), + Value::U8(value) => write!(f, "{value}"), + Value::U16(value) => write!(f, "{value}"), + Value::U32(value) => write!(f, "{value}"), + Value::U64(value) => write!(f, "{value}"), + Value::String(value) => write!(f, "{value}"), + Value::Function(_, _) => write!(f, "(function)"), + Value::Closure(_, _, _) => write!(f, "(closure)"), + Value::Tuple(fields) => { + let fields = vecmap(fields, ToString::to_string); + write!(f, "({})", fields.join(", ")) + } + Value::Struct(fields, typ) => { + let typename = match typ.follow_bindings() { + Type::Struct(def, _) => def.borrow().name.to_string(), + other => other.to_string(), + }; + let fields = vecmap(fields, |(name, value)| format!("{}: {}", name, value)); + write!(f, "{typename} {{ {} }}", fields.join(", ")) + } + Value::Pointer(value) => write!(f, "&mut {}", value.borrow()), + Value::Array(values, _) => { + let values = vecmap(values, ToString::to_string); + write!(f, "[{}]", values.join(", ")) + } + Value::Slice(values, _) => { + let values = vecmap(values, ToString::to_string); + write!(f, "&[{}]", values.join(", ")) + } + Value::Code(_) => todo!(), + } + } +} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 4988628db83..01f58ba4c27 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1642,7 +1642,9 @@ impl<'a> Resolver<'a> { // The quoted expression isn't resolved since we don't want errors if variables aren't defined ExpressionKind::Quote(block) => HirExpression::Quote(block), - ExpressionKind::Comptime(block) => HirExpression::Comptime(self.resolve_block(block)), + ExpressionKind::Comptime(block, _) => { + HirExpression::Comptime(self.resolve_block(block)) + } ExpressionKind::Resolved(_) => unreachable!( "ExpressionKind::Resolved should only be emitted by the comptime interpreter" ), diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index fdda271e79c..950697c2536 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -702,20 +702,27 @@ pub enum FunctionAttribute { } impl FunctionAttribute { - pub fn builtin(self) -> Option { + pub fn builtin(&self) -> Option<&String> { match self { FunctionAttribute::Builtin(name) => Some(name), _ => None, } } - pub fn foreign(self) -> Option { + pub fn foreign(&self) -> Option<&String> { match self { FunctionAttribute::Foreign(name) => Some(name), _ => None, } } + pub fn oracle(&self) -> Option<&String> { + match self { + FunctionAttribute::Oracle(name) => Some(name), + _ => None, + } + } + pub fn is_foreign(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_)) } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index a25d6488c83..7ecea5c9eac 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -18,7 +18,6 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - token::FunctionAttribute, Type, TypeBinding, TypeBindings, TypeVariable, TypeVariableKind, }; use acvm::{acir::AcirField, FieldElement}; @@ -216,18 +215,18 @@ impl<'interner> Monomorphizer<'interner> { let attributes = self.interner.function_attributes(&id); match self.interner.function_meta(&id).kind { FunctionKind::LowLevel => { - let attribute = attributes.function.clone().expect("all low level functions must contain a function attribute which contains the opcode which it links to"); + let attribute = attributes.function.as_ref().expect("all low level functions must contain a function attribute which contains the opcode which it links to"); let opcode = attribute.foreign().expect( "ice: function marked as foreign, but attribute kind does not match this", ); - Definition::LowLevel(opcode) + Definition::LowLevel(opcode.to_string()) } FunctionKind::Builtin => { - let attribute = attributes.function.clone().expect("all low level functions must contain a function attribute which contains the opcode which it links to"); + let attribute = attributes.function.as_ref().expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); let opcode = attribute.builtin().expect( "ice: function marked as builtin, but attribute kind does not match this", ); - Definition::Builtin(opcode) + Definition::Builtin(opcode.to_string()) } FunctionKind::Normal => { let id = @@ -235,15 +234,11 @@ impl<'interner> Monomorphizer<'interner> { Definition::Function(id) } FunctionKind::Oracle => { - let attr = attributes - .function - .clone() - .expect("Oracle function must have an oracle attribute"); - - match attr { - FunctionAttribute::Oracle(name) => Definition::Oracle(name), - _ => unreachable!("Oracle function must have an oracle attribute"), - } + let attribute = attributes.function.as_ref().expect("all oracle functions must contain a function attribute which contains the opcode which it links to"); + let opcode = attribute.oracle().expect( + "ice: function marked as builtin, but attribute kind does not match this", + ); + Definition::Oracle(opcode.to_string()) } FunctionKind::Recursive => { unreachable!("Only main can be specified as recursive, which should already be checked"); diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 702ea79af9d..cabc788e07d 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -545,7 +545,9 @@ fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a where S: NoirParser + 'a, { - keyword(Keyword::Comptime).ignore_then(block(statement)).map(ExpressionKind::Comptime) + keyword(Keyword::Comptime) + .ignore_then(spanned(block(statement))) + .map(|(block, span)| ExpressionKind::Comptime(block, span)) } fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a diff --git a/test_programs/execution_success/comptime_println/Nargo.toml b/test_programs/execution_success/comptime_println/Nargo.toml new file mode 100644 index 00000000000..7f8ae3a9cb9 --- /dev/null +++ b/test_programs/execution_success/comptime_println/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_println" +type = "bin" +authors = [""] +compiler_version = ">=0.27.0" + +[dependencies] diff --git a/test_programs/execution_success/comptime_println/src/main.nr b/test_programs/execution_success/comptime_println/src/main.nr new file mode 100644 index 00000000000..f9770066c04 --- /dev/null +++ b/test_programs/execution_success/comptime_println/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + let x = comptime { + println("hello from compile-time!"); + 1 + 2 + }; + println(x); +} diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 9a704717ade..7ff943aea62 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -167,8 +167,8 @@ pub(crate) fn rewrite( } ExpressionKind::Lambda(_) => visitor.slice(span).to_string(), ExpressionKind::Quote(block) => format!("quote {}", rewrite_block(visitor, block, span)), - ExpressionKind::Comptime(block) => { - format!("comptime {}", rewrite_block(visitor, block, span)) + ExpressionKind::Comptime(block, block_span) => { + format!("comptime {}", rewrite_block(visitor, block, block_span)) } ExpressionKind::Error => unreachable!(), ExpressionKind::Resolved(_) => { From 58c7532da8dd86ee02b20d7e7809f5437f667845 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:14:12 +0200 Subject: [PATCH 04/57] fix: error for allocate instructions in acir-gen (#5200) # Description ## Problem\* Resolves #5175 ## Summary\* When we cannot resolve some references to an array, its allocate is not simplified and we get a panic. I changed this to an error message saying that we could not resolve all references from the array. I believe an error message is better than the panic, however I am not sure whether having remaining allocates only happens because of this case. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/errors.rs | 5 ++++- compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 1e922060100..dd63a254a18 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -43,6 +43,8 @@ pub enum RuntimeError { UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, #[error("All `oracle` methods should be wrapped in an unconstrained fn")] UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, + #[error("Could not resolve some references to the array. All references must be resolved at compile time")] + UnknownReference { call_stack: CallStack }, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -123,7 +125,8 @@ impl RuntimeError { | RuntimeError::NestedSlice { call_stack, .. } | RuntimeError::BigIntModulus { call_stack, .. } | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } - | RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } => call_stack, + | RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } + | RuntimeError::UnknownReference { call_stack } => call_stack, } } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 2aac083d727..40170e58a30 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -682,7 +682,9 @@ impl<'a> Context<'a> { self.handle_array_operation(instruction_id, dfg)?; } Instruction::Allocate => { - unreachable!("Expected all allocate instructions to be removed before acir_gen") + return Err(RuntimeError::UnknownReference { + call_stack: self.acir_context.get_call_stack().clone(), + }); } Instruction::Store { .. } => { unreachable!("Expected all store instructions to be removed before acir_gen") @@ -1307,6 +1309,9 @@ impl<'a> Context<'a> { } Ok(AcirValue::Array(values)) } + Type::Reference(reference_type) => { + self.array_get_value(reference_type.as_ref(), block_id, var_index) + } _ => unreachable!("ICE: Expected an array or numeric but got {ssa_type:?}"), } } From 17c859d58da695d64f6a4e203b3ed40bf98df64d Mon Sep 17 00:00:00 2001 From: ricecodekhmer <151774852+ricecodekhmer@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:17:36 +0100 Subject: [PATCH 05/57] chore(docs): fix incorrect docs github link in footer (#5206) # Description ## Problem\* This PR solves a broken link in the project documentation that incorrectly points users to an obsolete GitHub repository URL. ## Summary\* This change corrects the 'Docs GitHub' hyperlink in the footer of the documentation to direct users to the new correct repository path. Before, the link directed users to [https://github.com/noir-lang/docs](https://github.com/noir-lang/docs), which has been archived. The updated link now correctly points to [https://github.com/noir-lang/noir/tree/master/docs](https://github.com/noir-lang/noir/tree/master/docs). ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- docs/docusaurus.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 2d5b8941f55..29f612b0109 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -109,7 +109,7 @@ export default { }, { label: 'Docs GitHub', - href: 'https://github.com/noir-lang/docs', + href: 'https://github.com/noir-lang/noir/tree/master/docs', }, ], }, From 14adafc965fa9c833e096ec037e086aae67703ad Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:42:24 +0100 Subject: [PATCH 06/57] feat!: add session id to foreign call RPC requests (#5205) # Description ## Problem\* Resolves ## Summary\* This PR adds an `id` field to RPC foreign call requests so that the TriXE is able to maintain separate internal state for different test functions so that Aztec integration tests can be run in parallel. This has necessitated some changes to the request format so this is a breaking change. The RPC request now looks like ``` '{ "jsonrpc":"2.0", "method":"resolve_foreign_call", "params":[{ "session_id":3789997497881369652, "function": "foo", "inputs": [ "0000000000000000000000000000000000000000000000000000000000000001", ["0000000000000000000000000000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000002" ]] }]} ``` ## Additional Context ## Documentation\* Check one: - [] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 2 + Cargo.toml | 1 + acvm-repo/acvm/Cargo.toml | 3 +- acvm-repo/acvm/src/pwg/brillig.rs | 3 +- acvm-repo/brillig/src/foreign_call.rs | 1 + docs/docs/how_to/how-to-oracles.md | 17 ++-- tooling/acvm_cli/Cargo.toml | 2 +- tooling/nargo/Cargo.toml | 1 + tooling/nargo/src/ops/foreign_calls.rs | 109 +++++++++++++++++++------ 9 files changed, 101 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61bfdd08c0e..8e40315cdd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "paste", "proptest", "rand 0.8.5", + "serde", "thiserror", "tracing", ] @@ -2485,6 +2486,7 @@ dependencies = [ "noirc_errors", "noirc_frontend", "noirc_printable_type", + "rand 0.8.5", "rayon", "serde", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 182580f8d67..d23089adb56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,6 +131,7 @@ similar-asserts = "1.5.0" tempfile = "3.6.0" jsonrpc = { version = "0.16.0", features = ["minreq_http"] } flate2 = "1.0.24" +rand = "0.8.5" im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index 577978939b0..0bd6ab6b7c7 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -16,6 +16,7 @@ repository.workspace = true num-bigint.workspace = true thiserror.workspace = true tracing.workspace = true +serde.workspace = true acir.workspace = true brillig_vm.workspace = true @@ -36,7 +37,7 @@ bls12_381 = [ ] [dev-dependencies] -rand = "0.8.5" +rand.workspace = true proptest = "1.2.0" paste = "1.0.14" ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } \ No newline at end of file diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 7e6c207b69a..3a639df044a 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -13,6 +13,7 @@ use acir::{ }; use acvm_blackbox_solver::BlackBoxFunctionSolver; use brillig_vm::{FailureReason, MemoryValue, VMStatus, VM}; +use serde::{Deserialize, Serialize}; use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; @@ -286,7 +287,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { /// where the result of the foreign call has not yet been provided. /// /// The caller must resolve this opcode externally based upon the information in the request. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct ForeignCallWaitInfo { /// An identifier interpreted by the caller process pub function: String, diff --git a/acvm-repo/brillig/src/foreign_call.rs b/acvm-repo/brillig/src/foreign_call.rs index a439d5c3202..9a45a4d2f20 100644 --- a/acvm-repo/brillig/src/foreign_call.rs +++ b/acvm-repo/brillig/src/foreign_call.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; /// Single output of a [foreign call][crate::Opcode::ForeignCall]. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] +#[serde(untagged)] pub enum ForeignCallParam { Single(F), Array(Vec), diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 2811968c634..62ead1f534f 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -137,24 +137,17 @@ app.listen(5555); Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: ```js -server.addMethod("getSqrt", async (params) => { - const values = params[0].Array.map((field) => { +server.addMethod("resolve_function_call", async (params) => { + if params.function !== "getSqrt" { + throw Error("Unexpected foreign call") + }; + const values = params.inputs[0].Array.map((field) => { return `${Math.sqrt(parseInt(field, 16))}`; }); return { values: [{ Array: values }] }; }); ``` -:::tip - -Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a field element *as a string*. For example: - -```json -{ "values": [{ "Array": ["1", "2"] }]} -{ "values": [{ "Single": "1" }]} -{ "values": [{ "Single": "1" }, { "Array": ["1", "2"] }]} -``` - If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: ```js diff --git a/tooling/acvm_cli/Cargo.toml b/tooling/acvm_cli/Cargo.toml index 72424405d36..1cfd1f3b270 100644 --- a/tooling/acvm_cli/Cargo.toml +++ b/tooling/acvm_cli/Cargo.toml @@ -33,6 +33,6 @@ tracing-subscriber.workspace = true tracing-appender = "0.2.3" [dev-dependencies] -rand = "0.8.5" +rand.workspace = true proptest = "1.2.0" paste = "1.0.14" diff --git a/tooling/nargo/Cargo.toml b/tooling/nargo/Cargo.toml index 48047d10ea6..8abec267d20 100644 --- a/tooling/nargo/Cargo.toml +++ b/tooling/nargo/Cargo.toml @@ -24,6 +24,7 @@ codespan-reporting.workspace = true tracing.workspace = true rayon = "1.8.0" jsonrpc.workspace = true +rand.workspace = true [dev-dependencies] # TODO: This dependency is used to generate unit tests for `get_all_paths_in_dir` diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index c6b284beb13..115337c1636 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -5,6 +5,8 @@ use acvm::{ }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; +use rand::Rng; +use serde::{Deserialize, Serialize}; pub trait ForeignCallExecutor { fn execute( @@ -96,6 +98,12 @@ impl MockedCall { #[derive(Debug, Default)] pub struct DefaultForeignCallExecutor { + /// A randomly generated id for this `DefaultForeignCallExecutor`. + /// + /// This is used so that a single `external_resolver` can distinguish between requests from multiple + /// instantiations of `DefaultForeignCallExecutor`. + id: u64, + /// Mocks have unique ids used to identify them in Noir, allowing to update or remove them. last_mock_id: usize, /// The registered mocks @@ -106,6 +114,20 @@ pub struct DefaultForeignCallExecutor { external_resolver: Option, } +#[derive(Debug, Serialize, Deserialize)] +struct ResolveForeignCallRequest { + /// A session ID which allows the external RPC server to link this foreign call request to other foreign calls + /// for the same program execution. + /// + /// This is intended to allow a single RPC server to maintain state related to multiple program executions being + /// performed in parallel. + session_id: u64, + + #[serde(flatten)] + /// The foreign call which the external RPC server is to provide a response for. + function_call: ForeignCallWaitInfo, +} + impl DefaultForeignCallExecutor { pub fn new(show_output: bool, resolver_url: Option<&str>) -> Self { let oracle_resolver = resolver_url.map(|resolver_url| { @@ -123,6 +145,7 @@ impl DefaultForeignCallExecutor { DefaultForeignCallExecutor { show_output, external_resolver: oracle_resolver, + id: rand::thread_rng().gen(), ..DefaultForeignCallExecutor::default() } } @@ -275,10 +298,13 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } else if let Some(external_resolver) = &self.external_resolver { // If the user has registered an external resolver then we forward any remaining oracle calls there. - let encoded_params: Vec<_> = - foreign_call.inputs.iter().map(build_json_rpc_arg).collect(); + let encoded_params = vec![build_json_rpc_arg(ResolveForeignCallRequest { + session_id: self.id, + function_call: foreign_call.clone(), + })]; - let req = external_resolver.build_request(foreign_call_name, &encoded_params); + let req = + external_resolver.build_request("resolve_foreign_call", &encoded_params); let response = external_resolver.send_request(req)?; @@ -312,43 +338,49 @@ mod tests { use crate::ops::{DefaultForeignCallExecutor, ForeignCallExecutor}; + use super::ResolveForeignCallRequest; + #[allow(unreachable_pub)] #[rpc] pub trait OracleResolver { - #[rpc(name = "echo")] - fn echo( - &self, - param: ForeignCallParam, - ) -> RpcResult>; - - #[rpc(name = "sum")] - fn sum( + #[rpc(name = "resolve_foreign_call")] + fn resolve_foreign_call( &self, - array: ForeignCallParam, + req: ResolveForeignCallRequest, ) -> RpcResult>; } struct OracleResolverImpl; - impl OracleResolver for OracleResolverImpl { - fn echo( - &self, - param: ForeignCallParam, - ) -> RpcResult> { - Ok(vec![param].into()) + impl OracleResolverImpl { + fn echo(&self, param: ForeignCallParam) -> ForeignCallResult { + vec![param].into() } - fn sum( - &self, - array: ForeignCallParam, - ) -> RpcResult> { + fn sum(&self, array: ForeignCallParam) -> ForeignCallResult { let mut res: FieldElement = 0_usize.into(); for value in array.fields() { res += value; } - Ok(res.into()) + res.into() + } + } + + impl OracleResolver for OracleResolverImpl { + fn resolve_foreign_call( + &self, + req: ResolveForeignCallRequest, + ) -> RpcResult> { + let response = match req.function_call.function.as_str() { + "sum" => self.sum(req.function_call.inputs[0].clone()), + "echo" => self.echo(req.function_call.inputs[0].clone()), + "id" => FieldElement::from(req.session_id as u128).into(), + + _ => panic!("unexpected foreign call"), + }; + Ok(response) } } @@ -398,4 +430,35 @@ mod tests { server.close(); } + + #[test] + fn foreign_call_executor_id_is_persistent() { + let (server, url) = build_oracle_server(); + + let mut executor = DefaultForeignCallExecutor::new(false, Some(&url)); + + let foreign_call = ForeignCallWaitInfo { function: "id".to_string(), inputs: Vec::new() }; + + let result_1 = executor.execute(&foreign_call).unwrap(); + let result_2 = executor.execute(&foreign_call).unwrap(); + assert_eq!(result_1, result_2); + + server.close(); + } + + #[test] + fn oracle_resolver_rpc_can_distinguish_executors() { + let (server, url) = build_oracle_server(); + + let mut executor_1 = DefaultForeignCallExecutor::new(false, Some(&url)); + let mut executor_2 = DefaultForeignCallExecutor::new(false, Some(&url)); + + let foreign_call = ForeignCallWaitInfo { function: "id".to_string(), inputs: Vec::new() }; + + let result_1 = executor_1.execute(&foreign_call).unwrap(); + let result_2 = executor_2.execute(&foreign_call).unwrap(); + assert_ne!(result_1, result_2); + + server.close(); + } } From 3ec7ce4b74d37721fe025a97a438e4b4df85c040 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:34:00 +0100 Subject: [PATCH 07/57] chore: make `nargo` crate and debug info generic (#5184) # Description ## Problem\* Related to #5055 ## Summary\* This PR throws generics into more of the codebase. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_printable_type/src/lib.rs | 58 +++++++++--------- tooling/acvm_cli/src/errors.rs | 3 +- tooling/debugger/src/context.rs | 6 +- tooling/debugger/src/foreign_calls.rs | 16 ++--- tooling/debugger/src/lib.rs | 2 +- tooling/debugger/src/repl.rs | 2 +- tooling/nargo/src/artifacts/debug_vars.rs | 30 +++++----- tooling/nargo/src/errors.rs | 22 +++---- tooling/nargo/src/ops/execute.rs | 53 +++++++++-------- tooling/nargo/src/ops/foreign_calls.rs | 71 ++++++++++++----------- tooling/nargo/src/ops/test.rs | 2 +- tooling/nargo_cli/src/errors.rs | 4 +- tooling/noirc_abi/src/lib.rs | 6 +- 13 files changed, 137 insertions(+), 138 deletions(-) diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index d9c9c7f9731..dfecd301b35 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, str}; -use acvm::{acir::AcirField, brillig_vm::brillig::ForeignCallParam, FieldElement}; +use acvm::{acir::AcirField, brillig_vm::brillig::ForeignCallParam}; use iter_extended::vecmap; use regex::{Captures, Regex}; use serde::{Deserialize, Serialize}; @@ -51,19 +51,19 @@ pub enum PrintableType { /// For example, a toml file will parse into TomlTypes /// and those TomlTypes will be mapped to Value #[derive(Debug, Clone, Serialize, PartialEq)] -pub enum PrintableValue { - Field(FieldElement), +pub enum PrintableValue { + Field(F), String(String), - Vec { array_elements: Vec, is_slice: bool }, - Struct(BTreeMap), + Vec { array_elements: Vec>, is_slice: bool }, + Struct(BTreeMap>), Other, } /// In order to display a `PrintableValue` we need a `PrintableType` to accurately /// convert the value into a human-readable format. -pub enum PrintableValueDisplay { - Plain(PrintableValue, PrintableType), - FmtString(String, Vec<(PrintableValue, PrintableType)>), +pub enum PrintableValueDisplay { + Plain(PrintableValue, PrintableType), + FmtString(String, Vec<(PrintableValue, PrintableType)>), } #[derive(Debug, Error)] @@ -81,12 +81,10 @@ pub enum ForeignCallError { ResolvedAssertMessage(String), } -impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { +impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { type Error = ForeignCallError; - fn try_from( - foreign_call_inputs: &[ForeignCallParam], - ) -> Result { + fn try_from(foreign_call_inputs: &[ForeignCallParam]) -> Result { let (is_fmt_str, foreign_call_inputs) = foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?; @@ -98,9 +96,9 @@ impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { } } -fn convert_string_inputs( - foreign_call_inputs: &[ForeignCallParam], -) -> Result { +fn convert_string_inputs( + foreign_call_inputs: &[ForeignCallParam], +) -> Result, ForeignCallError> { // Fetch the PrintableType from the foreign call input // The remaining input values should hold what is to be printed let (printable_type_as_values, input_values) = @@ -115,9 +113,9 @@ fn convert_string_inputs( Ok(PrintableValueDisplay::Plain(value, printable_type)) } -fn convert_fmt_string_inputs( - foreign_call_inputs: &[ForeignCallParam], -) -> Result { +fn convert_fmt_string_inputs( + foreign_call_inputs: &[ForeignCallParam], +) -> Result, ForeignCallError> { let (message, input_and_printable_types) = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; @@ -144,8 +142,8 @@ fn convert_fmt_string_inputs( Ok(PrintableValueDisplay::FmtString(message_as_string, output)) } -fn fetch_printable_type( - printable_type: &ForeignCallParam, +fn fetch_printable_type( + printable_type: &ForeignCallParam, ) -> Result { let printable_type_as_fields = printable_type.fields(); let printable_type_as_string = decode_string_value(&printable_type_as_fields); @@ -154,7 +152,7 @@ fn fetch_printable_type( Ok(printable_type) } -fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { +fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { let mut output = String::new(); match (value, typ) { (PrintableValue::Field(f), PrintableType::Field) => { @@ -269,7 +267,7 @@ fn replace_all( Ok(new) } -impl std::fmt::Display for PrintableValueDisplay { +impl std::fmt::Display for PrintableValueDisplay { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Plain(value, typ) => { @@ -295,7 +293,7 @@ impl std::fmt::Display for PrintableValueDisplay { /// A singular '0' will be prepended as well if the trimmed string has an odd length. /// A hex string's length needs to be even to decode into bytes, as two digits correspond to /// one byte. -fn format_field_string(field: FieldElement) -> String { +fn format_field_string(field: F) -> String { if field.is_zero() { return "0x00".to_owned(); } @@ -306,11 +304,11 @@ fn format_field_string(field: FieldElement) -> String { "0x".to_owned() + &trimmed_field } -/// Assumes that `field_iterator` contains enough [FieldElement] in order to decode the [PrintableType] -pub fn decode_value( - field_iterator: &mut impl Iterator, +/// Assumes that `field_iterator` contains enough field elements in order to decode the [PrintableType] +pub fn decode_value( + field_iterator: &mut impl Iterator, typ: &PrintableType, -) -> PrintableValue { +) -> PrintableValue { match typ { PrintableType::Field | PrintableType::SignedInteger { .. } @@ -346,7 +344,7 @@ pub fn decode_value( is_slice: false, }, PrintableType::String { length } => { - let field_elements: Vec = field_iterator.take(*length as usize).collect(); + let field_elements: Vec = field_iterator.take(*length as usize).collect(); PrintableValue::String(decode_string_value(&field_elements)) } @@ -372,11 +370,11 @@ pub fn decode_value( // we decode the reference, but it's not really used for printing decode_value(field_iterator, typ) } - PrintableType::Unit => PrintableValue::Field(FieldElement::zero()), + PrintableType::Unit => PrintableValue::Field(F::zero()), } } -pub fn decode_string_value(field_elements: &[FieldElement]) -> String { +pub fn decode_string_value(field_elements: &[F]) -> String { // TODO: Replace with `into` when Char is supported let string_as_slice = vecmap(field_elements, |e| { let mut field_as_bytes = e.to_be_bytes(); diff --git a/tooling/acvm_cli/src/errors.rs b/tooling/acvm_cli/src/errors.rs index 8bc79347159..886c1bf80f2 100644 --- a/tooling/acvm_cli/src/errors.rs +++ b/tooling/acvm_cli/src/errors.rs @@ -1,3 +1,4 @@ +use acir::FieldElement; use nargo::NargoError; use std::path::PathBuf; use thiserror::Error; @@ -34,7 +35,7 @@ pub(crate) enum CliError { /// Error related to circuit execution #[error(transparent)] - CircuitExecutionError(#[from] NargoError), + CircuitExecutionError(#[from] NargoError), /// Input Witness Value Error #[error("Error: failed to parse witness value {0}")] diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 9dc5c758c6f..5d0916b4863 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -23,7 +23,7 @@ pub(super) enum DebugCommandResult { Done, Ok, BreakpointReached(OpcodeLocation), - Error(NargoError), + Error(NargoError), } pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { @@ -482,11 +482,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } - pub(super) fn get_variables(&self) -> Vec { + pub(super) fn get_variables(&self) -> Vec> { return self.foreign_call_executor.get_variables(); } - pub(super) fn current_stack_frame(&self) -> Option { + pub(super) fn current_stack_frame(&self) -> Option> { return self.foreign_call_executor.current_stack_frame(); } diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 6989936ae93..03b1a35dfa5 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -38,14 +38,14 @@ impl DebugForeignCall { } } -pub trait DebugForeignCallExecutor: ForeignCallExecutor { - fn get_variables(&self) -> Vec; - fn current_stack_frame(&self) -> Option; +pub trait DebugForeignCallExecutor: ForeignCallExecutor { + fn get_variables(&self) -> Vec>; + fn current_stack_frame(&self) -> Option>; } pub struct DefaultDebugForeignCallExecutor { - executor: DefaultForeignCallExecutor, - pub debug_vars: DebugVars, + executor: DefaultForeignCallExecutor, + pub debug_vars: DebugVars, } impl DefaultDebugForeignCallExecutor { @@ -73,11 +73,11 @@ impl DefaultDebugForeignCallExecutor { } impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { - fn get_variables(&self) -> Vec { + fn get_variables(&self) -> Vec> { self.debug_vars.get_variables() } - fn current_stack_frame(&self) -> Option { + fn current_stack_frame(&self) -> Option> { self.debug_vars.current_stack_frame() } } @@ -90,7 +90,7 @@ fn debug_fn_id(value: &FieldElement) -> DebugFnId { DebugFnId(value.to_u128() as u32) } -impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { +impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index d7a1337c82f..9168a6228f0 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -24,7 +24,7 @@ pub fn debug_circuit>( debug_artifact: DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &[BrilligBytecode], -) -> Result>, NargoError> { +) -> Result>, NargoError> { repl::run(blackbox_solver, circuit, &debug_artifact, initial_witness, unconstrained_functions) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 5aef12ad8d4..07f9333d51c 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -382,7 +382,7 @@ pub fn run>( debug_artifact: &DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &[BrilligBytecode], -) -> Result>, NargoError> { +) -> Result>, NargoError> { let context = RefCell::new(ReplDebugger::new( blackbox_solver, circuit, diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index d2c0bb3ea00..aa9328432b8 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -1,4 +1,4 @@ -use acvm::FieldElement; +use acvm::AcirField; use noirc_errors::debug_info::{ DebugFnId, DebugFunction, DebugInfo, DebugTypeId, DebugVarId, DebugVariable, }; @@ -6,31 +6,31 @@ use noirc_printable_type::{decode_value, PrintableType, PrintableValue}; use std::collections::HashMap; #[derive(Debug, Default, Clone)] -pub struct DebugVars { +pub struct DebugVars { variables: HashMap, functions: HashMap, types: HashMap, - frames: Vec<(DebugFnId, HashMap)>, + frames: Vec<(DebugFnId, HashMap>)>, } -pub struct StackFrame<'a> { +pub struct StackFrame<'a, F> { pub function_name: &'a str, pub function_params: Vec<&'a str>, - pub variables: Vec<(&'a str, &'a PrintableValue, &'a PrintableType)>, + pub variables: Vec<(&'a str, &'a PrintableValue, &'a PrintableType)>, } -impl DebugVars { +impl DebugVars { pub fn insert_debug_info(&mut self, info: &DebugInfo) { self.variables.extend(info.variables.clone()); self.types.extend(info.types.clone()); self.functions.extend(info.functions.clone()); } - pub fn get_variables(&self) -> Vec { + pub fn get_variables(&self) -> Vec> { self.frames.iter().map(|(fn_id, frame)| self.build_stack_frame(fn_id, frame)).collect() } - pub fn current_stack_frame(&self) -> Option { + pub fn current_stack_frame(&self) -> Option> { self.frames.last().map(|(fn_id, frame)| self.build_stack_frame(fn_id, frame)) } @@ -44,13 +44,13 @@ impl DebugVars { fn build_stack_frame<'a>( &'a self, fn_id: &DebugFnId, - frame: &'a HashMap, - ) -> StackFrame { + frame: &'a HashMap>, + ) -> StackFrame { let debug_fn = &self.functions.get(fn_id).expect("failed to find function metadata"); let params: Vec<&str> = debug_fn.arg_names.iter().map(|arg_name| arg_name.as_str()).collect(); - let vars: Vec<(&str, &PrintableValue, &PrintableType)> = frame + let vars: Vec<(&str, &PrintableValue, &PrintableType)> = frame .iter() .filter_map(|(var_id, var_value)| { self.lookup_var(*var_id).map(|(name, typ)| (name, var_value, typ)) @@ -64,7 +64,7 @@ impl DebugVars { } } - pub fn assign_var(&mut self, var_id: DebugVarId, values: &[FieldElement]) { + pub fn assign_var(&mut self, var_id: DebugVarId, values: &[F]) { let type_id = &self.variables.get(&var_id).unwrap().debug_type_id; let ptype = self.types.get(type_id).unwrap(); @@ -75,9 +75,9 @@ impl DebugVars { .insert(var_id, decode_value(&mut values.iter().copied(), ptype)); } - pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[FieldElement]) { + pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[F]) { let current_frame = &mut self.frames.last_mut().expect("unexpected empty stack frames").1; - let mut cursor: &mut PrintableValue = current_frame + let mut cursor: &mut PrintableValue = current_frame .get_mut(&var_id) .unwrap_or_else(|| panic!("value unavailable for var_id {var_id:?}")); let cursor_type_id = &self @@ -146,7 +146,7 @@ impl DebugVars { *cursor = decode_value(&mut values.iter().copied(), cursor_type); } - pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[FieldElement]) { + pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[F]) { unimplemented![] } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 200420e5ce5..b2248605cb5 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -6,7 +6,7 @@ use acvm::{ ResolvedOpcodeLocation, }, pwg::{ErrorLocation, OpcodeResolutionError}, - FieldElement, + AcirField, FieldElement, }; use noirc_abi::{display_abi_error, Abi, AbiErrorType}; use noirc_errors::{ @@ -40,21 +40,21 @@ impl From for CompileError { } #[derive(Debug, Error)] -pub enum NargoError { +pub enum NargoError { /// Error while compiling Noir into ACIR. #[error("Failed to compile circuit")] CompilationError, /// ACIR circuit execution error #[error(transparent)] - ExecutionError(#[from] ExecutionError), + ExecutionError(#[from] ExecutionError), /// Oracle handling error #[error(transparent)] ForeignCallError(#[from] ForeignCallError), } -impl NargoError { +impl NargoError { /// Extracts the user defined failure message from the ExecutionError /// If one exists. /// @@ -94,17 +94,17 @@ impl NargoError { } #[derive(Debug, Error)] -pub enum ExecutionError { +pub enum ExecutionError { #[error("Failed assertion")] - AssertionFailed(ResolvedAssertionPayload, Vec), + AssertionFailed(ResolvedAssertionPayload, Vec), #[error("Failed to solve program: '{}'", .0)] - SolvingError(OpcodeResolutionError, Option>), + SolvingError(OpcodeResolutionError, Option>), } /// Extracts the opcode locations from a nargo error. -fn extract_locations_from_error( - error: &ExecutionError, +fn extract_locations_from_error( + error: &ExecutionError, debug: &[DebugInfo], ) -> Option> { let mut opcode_locations = match error { @@ -163,7 +163,7 @@ fn extract_locations_from_error( fn extract_message_from_error( error_types: &BTreeMap, - nargo_err: &NargoError, + nargo_err: &NargoError, ) -> String { match nargo_err { NargoError::ExecutionError(ExecutionError::AssertionFailed( @@ -198,7 +198,7 @@ fn extract_message_from_error( /// Tries to generate a runtime diagnostic from a nargo error. It will successfully do so if it's a runtime error with a call stack. pub fn try_to_diagnose_runtime_error( - nargo_err: &NargoError, + nargo_err: &NargoError, abi: &Abi, debug: &[DebugInfo], ) -> Option { diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 42e93e0e3cf..c9cc60d03d9 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -5,24 +5,24 @@ use acvm::acir::circuit::{ use acvm::acir::native_types::WitnessStack; use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ACVM}; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; -use acvm::{BlackBoxFunctionSolver, FieldElement}; +use acvm::{AcirField, BlackBoxFunctionSolver}; use crate::errors::ExecutionError; use crate::NargoError; use super::foreign_calls::ForeignCallExecutor; -struct ProgramExecutor<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> { - functions: &'a [Circuit], +struct ProgramExecutor<'a, F, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { + functions: &'a [Circuit], - unconstrained_functions: &'a [BrilligBytecode], + unconstrained_functions: &'a [BrilligBytecode], // This gets built as we run through the program looking at each function call - witness_stack: WitnessStack, + witness_stack: WitnessStack, blackbox_solver: &'a B, - foreign_call_executor: &'a mut F, + foreign_call_executor: &'a mut E, // The Noir compiler codegens per function and call stacks are not shared across ACIR function calls. // We must rebuild a call stack when executing a program of many circuits. @@ -34,14 +34,14 @@ struct ProgramExecutor<'a, B: BlackBoxFunctionSolver, F: ForeignCa current_function_index: usize, } -impl<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> - ProgramExecutor<'a, B, F> +impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> + ProgramExecutor<'a, F, B, E> { fn new( - functions: &'a [Circuit], - unconstrained_functions: &'a [BrilligBytecode], + functions: &'a [Circuit], + unconstrained_functions: &'a [BrilligBytecode], blackbox_solver: &'a B, - foreign_call_executor: &'a mut F, + foreign_call_executor: &'a mut E, ) -> Self { ProgramExecutor { functions, @@ -54,15 +54,15 @@ impl<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> } } - fn finalize(self) -> WitnessStack { + fn finalize(self) -> WitnessStack { self.witness_stack } #[tracing::instrument(level = "trace", skip_all)] fn execute_circuit( &mut self, - initial_witness: WitnessMap, - ) -> Result, NargoError> { + initial_witness: WitnessMap, + ) -> Result, NargoError> { let circuit = &self.functions[self.current_function_index]; let mut acvm = ACVM::new( self.blackbox_solver, @@ -109,14 +109,13 @@ impl<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> _ => None, }; - let assertion_payload: Option> = - match &error { - OpcodeResolutionError::BrilligFunctionFailed { payload, .. } - | OpcodeResolutionError::UnsatisfiedConstrain { payload, .. } => { - payload.clone() - } - _ => None, - }; + let assertion_payload: Option> = match &error { + OpcodeResolutionError::BrilligFunctionFailed { payload, .. } + | OpcodeResolutionError::UnsatisfiedConstrain { payload, .. } => { + payload.clone() + } + _ => None, + }; return Err(NargoError::ExecutionError(match assertion_payload { Some(payload) => ExecutionError::AssertionFailed( @@ -174,12 +173,12 @@ impl<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> } #[tracing::instrument(level = "trace", skip_all)] -pub fn execute_program, F: ForeignCallExecutor>( - program: &Program, - initial_witness: WitnessMap, +pub fn execute_program, E: ForeignCallExecutor>( + program: &Program, + initial_witness: WitnessMap, blackbox_solver: &B, - foreign_call_executor: &mut F, -) -> Result, NargoError> { + foreign_call_executor: &mut E, +) -> Result, NargoError> { let mut executor = ProgramExecutor::new( &program.functions, &program.unconstrained_functions, diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 115337c1636..987c7dd9cb9 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,18 +1,18 @@ use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, FieldElement, + AcirField, }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; use rand::Rng; use serde::{Deserialize, Serialize}; -pub trait ForeignCallExecutor { +pub trait ForeignCallExecutor { fn execute( &mut self, - foreign_call: &ForeignCallWaitInfo, - ) -> Result, ForeignCallError>; + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError>; } /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. @@ -62,22 +62,22 @@ impl ForeignCall { /// This struct represents an oracle mock. It can be used for testing programs that use oracles. #[derive(Debug, PartialEq, Eq, Clone)] -struct MockedCall { +struct MockedCall { /// The id of the mock, used to update or remove it id: usize, /// The oracle it's mocking name: String, /// Optionally match the parameters - params: Option>>, + params: Option>>, /// The parameters with which the mock was last called - last_called_params: Option>>, + last_called_params: Option>>, /// The result to return when this mock is called - result: ForeignCallResult, + result: ForeignCallResult, /// How many times should this mock be called before it is removed times_left: Option, } -impl MockedCall { +impl MockedCall { fn new(id: usize, name: String) -> Self { Self { id, @@ -90,14 +90,14 @@ impl MockedCall { } } -impl MockedCall { - fn matches(&self, name: &str, params: &[ForeignCallParam]) -> bool { +impl MockedCall { + fn matches(&self, name: &str, params: &[ForeignCallParam]) -> bool { self.name == name && (self.params.is_none() || self.params.as_deref() == Some(params)) } } #[derive(Debug, Default)] -pub struct DefaultForeignCallExecutor { +pub struct DefaultForeignCallExecutor { /// A randomly generated id for this `DefaultForeignCallExecutor`. /// /// This is used so that a single `external_resolver` can distinguish between requests from multiple @@ -107,7 +107,7 @@ pub struct DefaultForeignCallExecutor { /// Mocks have unique ids used to identify them in Noir, allowing to update or remove them. last_mock_id: usize, /// The registered mocks - mocked_responses: Vec, + mocked_responses: Vec>, /// Whether to print [`ForeignCall::Print`] output. show_output: bool, /// JSON RPC client to resolve foreign calls @@ -128,7 +128,7 @@ struct ResolveForeignCallRequest { function_call: ForeignCallWaitInfo, } -impl DefaultForeignCallExecutor { +impl DefaultForeignCallExecutor { pub fn new(show_output: bool, resolver_url: Option<&str>) -> Self { let oracle_resolver = resolver_url.map(|resolver_url| { let mut transport_builder = @@ -146,15 +146,16 @@ impl DefaultForeignCallExecutor { show_output, external_resolver: oracle_resolver, id: rand::thread_rng().gen(), - ..DefaultForeignCallExecutor::default() + mocked_responses: Vec::new(), + last_mock_id: 0, } } } -impl DefaultForeignCallExecutor { +impl DefaultForeignCallExecutor { fn extract_mock_id( - foreign_call_inputs: &[ForeignCallParam], - ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { + foreign_call_inputs: &[ForeignCallParam], + ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { let (id, params) = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; let id = @@ -163,22 +164,20 @@ impl DefaultForeignCallExecutor { Ok((id, params)) } - fn find_mock_by_id(&self, id: usize) -> Option<&MockedCall> { + fn find_mock_by_id(&self, id: usize) -> Option<&MockedCall> { self.mocked_responses.iter().find(|response| response.id == id) } - fn find_mock_by_id_mut(&mut self, id: usize) -> Option<&mut MockedCall> { + fn find_mock_by_id_mut(&mut self, id: usize) -> Option<&mut MockedCall> { self.mocked_responses.iter_mut().find(|response| response.id == id) } - fn parse_string(param: &ForeignCallParam) -> String { + fn parse_string(param: &ForeignCallParam) -> String { let fields: Vec<_> = param.fields().to_vec(); decode_string_value(&fields) } - fn execute_print( - foreign_call_inputs: &[ForeignCallParam], - ) -> Result<(), ForeignCallError> { + fn execute_print(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { let skip_newline = foreign_call_inputs[0].unwrap_field().is_zero(); let foreign_call_inputs = @@ -191,10 +190,10 @@ impl DefaultForeignCallExecutor { } fn format_printable_value( - foreign_call_inputs: &[ForeignCallParam], + foreign_call_inputs: &[ForeignCallParam], skip_newline: bool, ) -> Result { - let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; + let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; let result = format!("{display_values}{}", if skip_newline { "" } else { "\n" }); @@ -202,11 +201,13 @@ impl DefaultForeignCallExecutor { } } -impl ForeignCallExecutor for DefaultForeignCallExecutor { +impl Deserialize<'a>> ForeignCallExecutor + for DefaultForeignCallExecutor +{ fn execute( &mut self, - foreign_call: &ForeignCallWaitInfo, - ) -> Result, ForeignCallError> { + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError> { let foreign_call_name = foreign_call.function.as_str(); match ForeignCall::lookup(foreign_call_name) { Some(ForeignCall::Print) => { @@ -222,7 +223,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { self.mocked_responses.push(MockedCall::new(id, mock_oracle_name)); self.last_mock_id += 1; - Ok(FieldElement::from(id).into()) + Ok(F::from(id).into()) } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -308,7 +309,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { let response = external_resolver.send_request(req)?; - let parsed_response: ForeignCallResult = response.result()?; + let parsed_response: ForeignCallResult = response.result()?; Ok(parsed_response) } else { @@ -401,7 +402,7 @@ mod tests { fn test_oracle_resolver_echo() { let (server, url) = build_oracle_server(); - let mut executor = DefaultForeignCallExecutor::new(false, Some(&url)); + let mut executor = DefaultForeignCallExecutor::::new(false, Some(&url)); let foreign_call = ForeignCallWaitInfo { function: "echo".to_string(), @@ -435,7 +436,7 @@ mod tests { fn foreign_call_executor_id_is_persistent() { let (server, url) = build_oracle_server(); - let mut executor = DefaultForeignCallExecutor::new(false, Some(&url)); + let mut executor = DefaultForeignCallExecutor::::new(false, Some(&url)); let foreign_call = ForeignCallWaitInfo { function: "id".to_string(), inputs: Vec::new() }; @@ -450,8 +451,8 @@ mod tests { fn oracle_resolver_rpc_can_distinguish_executors() { let (server, url) = build_oracle_server(); - let mut executor_1 = DefaultForeignCallExecutor::new(false, Some(&url)); - let mut executor_2 = DefaultForeignCallExecutor::new(false, Some(&url)); + let mut executor_1 = DefaultForeignCallExecutor::::new(false, Some(&url)); + let mut executor_2 = DefaultForeignCallExecutor::::new(false, Some(&url)); let foreign_call = ForeignCallWaitInfo { function: "id".to_string(), inputs: Vec::new() }; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index ed45251ac8a..ace2e9f0d0c 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -76,7 +76,7 @@ fn test_status_program_compile_pass( test_function: &TestFunction, abi: Abi, debug: Vec, - circuit_execution: Result, NargoError>, + circuit_execution: Result, NargoError>, ) -> TestStatus { let circuit_execution_err = match circuit_execution { // Circuit execution was successful; ie no errors or unsatisfied constraints diff --git a/tooling/nargo_cli/src/errors.rs b/tooling/nargo_cli/src/errors.rs index 3e0b13a9cbc..b28012ae7aa 100644 --- a/tooling/nargo_cli/src/errors.rs +++ b/tooling/nargo_cli/src/errors.rs @@ -1,4 +1,4 @@ -use acvm::acir::native_types::WitnessStackError; +use acvm::{acir::native_types::WitnessStackError, FieldElement}; use nargo::{errors::CompileError, NargoError}; use nargo_toml::ManifestError; use noir_debugger::errors::DapError; @@ -54,7 +54,7 @@ pub(crate) enum CliError { /// Error from Nargo #[error(transparent)] - NargoError(#[from] NargoError), + NargoError(#[from] NargoError), /// Error from Manifest #[error(transparent)] diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index b1ddba26f15..134dd5e621e 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -620,10 +620,10 @@ impl AbiErrorType { } } -pub fn display_abi_error( - fields: &[FieldElement], +pub fn display_abi_error( + fields: &[F], error_type: AbiErrorType, -) -> PrintableValueDisplay { +) -> PrintableValueDisplay { match error_type { AbiErrorType::FmtString { length, item_types } => { let mut fields_iter = fields.iter().copied(); From 8a322998ee60875f7abe4dc222f8fdc20cb0ee0a Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:18:28 +0100 Subject: [PATCH 08/57] chore: remove stale comment (#5179) # Description ## Problem\* Resolves ## Summary\* We no longer use a backend here. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/build.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 25a69489788..fcc09653c7d 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -287,9 +287,6 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa #[test] fn compile_success_empty_{test_name}() {{ - // We use a mocked backend for this test as we do not rely on the returned circuit size - // but we must call a backend as part of querying the number of opcodes in the circuit. - let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); From b1298b85f401b380e2d36c61bc1f5f63bfe6742e Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:34:42 +0100 Subject: [PATCH 09/57] chore: move implementation of bitwise operations into `blackbox_solver` (#5209) # Description ## Problem\* Related to #5055 ## Summary\* We don't need the field element to know about AND and XOR as we just need to be able to convert into byte arrays and back. I've then moved this into the blackbox solver crate (the only place we were actually using this) to simplify the `AcirField` trait. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/acir_field/src/field_element.rs | 81 -------------------- acvm-repo/acir_field/src/generic_ark.rs | 3 - acvm-repo/acvm/src/pwg/blackbox/logic.rs | 9 ++- acvm-repo/acvm_js/src/black_box_solvers.rs | 4 +- acvm-repo/blackbox_solver/src/lib.rs | 2 + acvm-repo/blackbox_solver/src/logic.rs | 87 ++++++++++++++++++++++ 6 files changed, 96 insertions(+), 90 deletions(-) create mode 100644 acvm-repo/blackbox_solver/src/logic.rs diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index 7810578ff73..81582b9fbac 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -195,15 +195,6 @@ impl FieldElement { Some(FieldElement(fr)) } - // mask_to methods will not remove any bytes from the field - // they are simply zeroed out - // Whereas truncate_to will remove those bits and make the byte array smaller - fn mask_to_be_bytes(&self, num_bits: u32) -> Vec { - let mut bytes = self.to_be_bytes(); - mask_vector_le(&mut bytes, num_bits as usize); - bytes - } - fn bits(&self) -> Vec { fn byte_to_bit(byte: u8) -> Vec { let mut bits = Vec::with_capacity(8); @@ -220,29 +211,6 @@ impl FieldElement { } bits } - - fn and_xor(&self, rhs: &FieldElement, num_bits: u32, is_xor: bool) -> FieldElement { - // XXX: Gadgets like SHA256 need to have their input be a multiple of 8 - // This is not a restriction caused by SHA256, as it works on bits - // but most backends assume bytes. - // We could implicitly pad, however this may not be intuitive for users. - // assert!( - // num_bits % 8 == 0, - // "num_bits is not a multiple of 8, it is {}", - // num_bits - // ); - - let lhs_bytes = self.mask_to_be_bytes(num_bits); - let rhs_bytes = rhs.mask_to_be_bytes(num_bits); - - let and_byte_arr: Vec<_> = lhs_bytes - .into_iter() - .zip(rhs_bytes) - .map(|(lhs, rhs)| if is_xor { lhs ^ rhs } else { lhs & rhs }) - .collect(); - - FieldElement::from_be_bytes_reduce(&and_byte_arr) - } } impl AcirField for FieldElement { @@ -376,13 +344,6 @@ impl AcirField for FieldElement { bytes[0..num_elements].to_vec() } - - fn and(&self, rhs: &FieldElement, num_bits: u32) -> FieldElement { - self.and_xor(rhs, num_bits, false) - } - fn xor(&self, rhs: &FieldElement, num_bits: u32) -> FieldElement { - self.and_xor(rhs, num_bits, true) - } } impl Neg for FieldElement { @@ -433,35 +394,6 @@ impl SubAssign for FieldElement { } } -fn mask_vector_le(bytes: &mut [u8], num_bits: usize) { - // reverse to big endian format - bytes.reverse(); - - let mask_power = num_bits % 8; - let array_mask_index = num_bits / 8; - - for (index, byte) in bytes.iter_mut().enumerate() { - match index.cmp(&array_mask_index) { - std::cmp::Ordering::Less => { - // do nothing if the current index is less than - // the array index. - } - std::cmp::Ordering::Equal => { - let mask = 2u8.pow(mask_power as u32) - 1; - // mask the byte - *byte &= mask; - } - std::cmp::Ordering::Greater => { - // Anything greater than the array index - // will be set to zero - *byte = 0; - } - } - } - // reverse back to little endian - bytes.reverse(); -} - // For pretty printing powers fn superscript(n: u64) -> String { if n == 0 { @@ -495,19 +427,6 @@ fn superscript(n: u64) -> String { mod tests { use super::{AcirField, FieldElement}; - #[test] - fn and() { - let max = 10_000u32; - - let num_bits = (std::mem::size_of::() * 8) as u32 - max.leading_zeros(); - - for x in 0..max { - let x = FieldElement::::from(x as i128); - let res = x.and(&x, num_bits); - assert_eq!(res.to_be_bytes(), x.to_be_bytes()); - } - } - #[test] fn serialize_fixed_test_vectors() { // Serialized field elements from of 0, -1, -2, -3 diff --git a/acvm-repo/acir_field/src/generic_ark.rs b/acvm-repo/acir_field/src/generic_ark.rs index 3d93de5a992..f7228e66314 100644 --- a/acvm-repo/acir_field/src/generic_ark.rs +++ b/acvm-repo/acir_field/src/generic_ark.rs @@ -78,7 +78,4 @@ pub trait AcirField: /// Returns the closest number of bytes to the bits specified /// This method truncates fn fetch_nearest_bytes(&self, num_bits: usize) -> Vec; - - fn and(&self, rhs: &Self, num_bits: u32) -> Self; - fn xor(&self, rhs: &Self, num_bits: u32) -> Self; } diff --git a/acvm-repo/acvm/src/pwg/blackbox/logic.rs b/acvm-repo/acvm/src/pwg/blackbox/logic.rs index 6e2ade3c49b..5be888c8ac6 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/logic.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/logic.rs @@ -5,6 +5,7 @@ use acir::{ native_types::{Witness, WitnessMap}, AcirField, }; +use acvm_blackbox_solver::{bit_and, bit_xor}; /// Solves a [`BlackBoxFunc::And`][acir::circuit::black_box_functions::BlackBoxFunc::AND] opcode and inserts /// the result into the supplied witness map @@ -19,7 +20,7 @@ pub(super) fn and( "number of bits specified for each input must be the same" ); solve_logic_opcode(initial_witness, &lhs.witness, &rhs.witness, *output, |left, right| { - left.and(right, lhs.num_bits) + bit_and(left, right, lhs.num_bits) }) } @@ -36,7 +37,7 @@ pub(super) fn xor( "number of bits specified for each input must be the same" ); solve_logic_opcode(initial_witness, &lhs.witness, &rhs.witness, *output, |left, right| { - left.xor(right, lhs.num_bits) + bit_xor(left, right, lhs.num_bits) }) } @@ -46,11 +47,11 @@ fn solve_logic_opcode( a: &Witness, b: &Witness, result: Witness, - logic_op: impl Fn(&F, &F) -> F, + logic_op: impl Fn(F, F) -> F, ) -> Result<(), OpcodeResolutionError> { let w_l_value = witness_to_value(initial_witness, *a)?; let w_r_value = witness_to_value(initial_witness, *b)?; - let assignment = logic_op(w_l_value, w_r_value); + let assignment = logic_op(*w_l_value, *w_r_value); insert_value(&result, assignment, initial_witness) } diff --git a/acvm-repo/acvm_js/src/black_box_solvers.rs b/acvm-repo/acvm_js/src/black_box_solvers.rs index 4f2676f8d2f..4cd85a5ed87 100644 --- a/acvm-repo/acvm_js/src/black_box_solvers.rs +++ b/acvm-repo/acvm_js/src/black_box_solvers.rs @@ -9,7 +9,7 @@ use acvm::{acir::AcirField, FieldElement}; pub fn and(lhs: JsString, rhs: JsString) -> JsString { let lhs = js_value_to_field_element(lhs.into()).unwrap(); let rhs = js_value_to_field_element(rhs.into()).unwrap(); - let result = lhs.and(&rhs, FieldElement::max_num_bits()); + let result = acvm::blackbox_solver::bit_and(lhs, rhs, FieldElement::max_num_bits()); field_element_to_js_string(&result) } @@ -18,7 +18,7 @@ pub fn and(lhs: JsString, rhs: JsString) -> JsString { pub fn xor(lhs: JsString, rhs: JsString) -> JsString { let lhs = js_value_to_field_element(lhs.into()).unwrap(); let rhs = js_value_to_field_element(rhs.into()).unwrap(); - let result = lhs.xor(&rhs, FieldElement::max_num_bits()); + let result = acvm::blackbox_solver::bit_xor(lhs, rhs, FieldElement::max_num_bits()); field_element_to_js_string(&result) } diff --git a/acvm-repo/blackbox_solver/src/lib.rs b/acvm-repo/blackbox_solver/src/lib.rs index a68b52a2a62..c39deb64138 100644 --- a/acvm-repo/blackbox_solver/src/lib.rs +++ b/acvm-repo/blackbox_solver/src/lib.rs @@ -15,12 +15,14 @@ mod bigint; mod curve_specific_solver; mod ecdsa; mod hash; +mod logic; pub use aes128::aes128_encrypt; pub use bigint::BigIntSolver; pub use curve_specific_solver::{BlackBoxFunctionSolver, StubbedBlackBoxSolver}; pub use ecdsa::{ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; pub use hash::{blake2s, blake3, keccak256, keccakf1600, sha256, sha256compression}; +pub use logic::{bit_and, bit_xor}; #[derive(Clone, PartialEq, Eq, Debug, Error)] pub enum BlackBoxResolutionError { diff --git a/acvm-repo/blackbox_solver/src/logic.rs b/acvm-repo/blackbox_solver/src/logic.rs new file mode 100644 index 00000000000..95bd3669ff5 --- /dev/null +++ b/acvm-repo/blackbox_solver/src/logic.rs @@ -0,0 +1,87 @@ +use acir::AcirField; + +pub fn bit_and(lhs: F, rhs: F, num_bits: u32) -> F { + bitwise_op(lhs, rhs, num_bits, |lhs_byte, rhs_byte| lhs_byte & rhs_byte) +} + +pub fn bit_xor(lhs: F, rhs: F, num_bits: u32) -> F { + bitwise_op(lhs, rhs, num_bits, |lhs_byte, rhs_byte| lhs_byte ^ rhs_byte) +} + +fn bitwise_op(lhs: F, rhs: F, num_bits: u32, op: fn(u8, u8) -> u8) -> F { + // XXX: Gadgets like SHA256 need to have their input be a multiple of 8 + // This is not a restriction caused by SHA256, as it works on bits + // but most backends assume bytes. + // We could implicitly pad, however this may not be intuitive for users. + // assert!( + // num_bits % 8 == 0, + // "num_bits is not a multiple of 8, it is {}", + // num_bits + // ); + + let lhs_bytes = mask_to_be_bytes(lhs, num_bits); + let rhs_bytes = mask_to_be_bytes(rhs, num_bits); + + let and_byte_arr: Vec<_> = + lhs_bytes.into_iter().zip(rhs_bytes).map(|(left, right)| op(left, right)).collect(); + + F::from_be_bytes_reduce(&and_byte_arr) +} + +// mask_to methods will not remove any bytes from the field +// they are simply zeroed out +// Whereas truncate_to will remove those bits and make the byte array smaller +fn mask_to_be_bytes(field: F, num_bits: u32) -> Vec { + let mut bytes = field.to_be_bytes(); + mask_vector_le(&mut bytes, num_bits as usize); + bytes +} + +fn mask_vector_le(bytes: &mut [u8], num_bits: usize) { + // reverse to big endian format + bytes.reverse(); + + let mask_power = num_bits % 8; + let array_mask_index = num_bits / 8; + + for (index, byte) in bytes.iter_mut().enumerate() { + match index.cmp(&array_mask_index) { + std::cmp::Ordering::Less => { + // do nothing if the current index is less than + // the array index. + } + std::cmp::Ordering::Equal => { + let mask = 2u8.pow(mask_power as u32) - 1; + // mask the byte + *byte &= mask; + } + std::cmp::Ordering::Greater => { + // Anything greater than the array index + // will be set to zero + *byte = 0; + } + } + } + // reverse back to little endian + bytes.reverse(); +} + +#[cfg(test)] +mod tests { + use acir::{AcirField, FieldElement}; + + use crate::bit_and; + + #[test] + fn and() { + let max = 10_000u32; + + let num_bits = (std::mem::size_of::() * 8) as u32 - max.leading_zeros(); + + for x in 0..max { + let x = FieldElement::from(x as i128); + let res = bit_and(x, x, num_bits); + assert_eq!(res.to_be_bytes(), x.to_be_bytes()); + } + } +} From 21562aeea162d246573967115e7c519715f6d3d8 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:44:38 +0100 Subject: [PATCH 10/57] chore!: remove `param_witnesses` and `return_witnesses` from ABI (#5154) # Description ## Problem\* Followup to #5142 Related to #5104 ## Summary\* This PR deletes the `param_witnesses` and `return_witnesses` fields from the ABI as the locations of all inputs/return values are defined implicitly by the types themselves. I've left this as draft as there's some changes which need to be made to the JS code which will require some extra work (it needs to be made aware of the sizes of various types.) ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/test-js-packages.yml | 27 ---- compiler/noirc_driver/src/abi_gen.rs | 93 +------------ compiler/noirc_driver/src/lib.rs | 21 +-- .../getting_started/tooling/noir_codegen.md | 3 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 3 +- tooling/nargo_cli/src/cli/execute_cmd.rs | 3 +- .../noir_js_backend_barretenberg/package.json | 3 - .../noir_js_backend_barretenberg/src/index.ts | 1 - .../src/public_inputs.ts | 26 +--- .../test/public_input_deflattening.test.ts | 97 -------------- tooling/noir_js_types/src/types.ts | 2 - tooling/noirc_abi/src/input_parser/mod.rs | 3 - tooling/noirc_abi/src/lib.rs | 122 +++++------------- yarn.lock | 2 - 14 files changed, 45 insertions(+), 361 deletions(-) delete mode 100644 tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts diff --git a/.github/workflows/test-js-packages.yml b/.github/workflows/test-js-packages.yml index db162e21269..9f46e6f98e8 100644 --- a/.github/workflows/test-js-packages.yml +++ b/.github/workflows/test-js-packages.yml @@ -221,32 +221,6 @@ jobs: - name: Run browser tests run: yarn workspace @noir-lang/noirc_abi test:browser - test-noir-js-backend-barretenberg: - needs: [build-noirc-abi] - name: noir-js-backend-barretenberg - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download wasm package artifact - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: ./tooling/noirc_abi_wasm - - - name: Install Yarn dependencies - uses: ./.github/actions/setup - - - name: Build noir_js_types - run: yarn workspace @noir-lang/types build - - - name: Run barretenberg wrapper tests - run: | - yarn workspace @noir-lang/backend_barretenberg test - test-noir-js: needs: [build-nargo, build-acvm-js, build-noirc-abi] name: Noir JS @@ -546,7 +520,6 @@ jobs: - test-acvm_js-node - test-acvm_js-browser - test-noirc-abi - - test-noir-js-backend-barretenberg - test-noir-js - test-noir-wasm - test-noir-codegen diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 609c88b92c2..71dd1b18761 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -1,8 +1,7 @@ use std::collections::BTreeMap; use acvm::acir::circuit::ErrorSelector; -use acvm::acir::native_types::Witness; -use iter_extended::{btree_map, vecmap}; +use iter_extended::vecmap; use noirc_abi::{Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue}; use noirc_frontend::ast::Visibility; use noirc_frontend::{ @@ -11,27 +10,23 @@ use noirc_frontend::{ macros_api::{HirExpression, HirLiteral}, node_interner::{FuncId, NodeInterner}, }; -use std::ops::Range; /// Arranges a function signature and a generated circuit's return witnesses into a /// `noirc_abi::Abi`. pub(super) fn gen_abi( context: &Context, func_id: &FuncId, - input_witnesses: Vec, - return_witnesses: Vec, return_visibility: Visibility, error_types: BTreeMap, ) -> Abi { let (parameters, return_type) = compute_function_abi(context, func_id); - let param_witnesses = param_witnesses_from_abi_param(¶meters, input_witnesses); let return_type = return_type .map(|typ| AbiReturnType { abi_type: typ, visibility: return_visibility.into() }); let error_types = error_types .into_iter() .map(|(selector, typ)| (selector, AbiErrorType::from_type(context, &typ))) .collect(); - Abi { parameters, return_type, param_witnesses, return_witnesses, error_types } + Abi { parameters, return_type, error_types } } pub(super) fn compute_function_abi( @@ -67,55 +62,6 @@ fn into_abi_params(context: &Context, params: Vec) -> Vec { }) } -// Takes each abi parameter and shallowly maps to the expected witness range in which the -// parameter's constituent values live. -fn param_witnesses_from_abi_param( - abi_params: &[AbiParameter], - input_witnesses: Vec, -) -> BTreeMap>> { - let mut idx = 0_usize; - if input_witnesses.is_empty() { - return BTreeMap::new(); - } - - btree_map(abi_params, |param| { - let num_field_elements_needed = param.typ.field_count() as usize; - let param_witnesses = &input_witnesses[idx..idx + num_field_elements_needed]; - - // It's likely that `param_witnesses` will consist of mostly incrementing witness indices. - // We then want to collapse these into `Range`s to save space. - let param_witnesses = collapse_ranges(param_witnesses); - idx += num_field_elements_needed; - (param.name.clone(), param_witnesses) - }) -} - -/// Takes a vector of [`Witnesses`][`Witness`] and collapses it into a vector of [`Range`]s of [`Witnesses`][`Witness`]. -fn collapse_ranges(witnesses: &[Witness]) -> Vec> { - if witnesses.is_empty() { - return Vec::new(); - } - let mut wit = Vec::new(); - let mut last_wit: Witness = witnesses[0]; - - for (i, witness) in witnesses.iter().enumerate() { - if i == 0 { - continue; - }; - let witness_index = witness.witness_index(); - let prev_witness_index = witnesses[i - 1].witness_index(); - if witness_index != prev_witness_index + 1 { - wit.push(last_wit..Witness(prev_witness_index + 1)); - last_wit = *witness; - }; - } - - let last_witness = witnesses.last().unwrap().witness_index(); - wit.push(last_wit..Witness(last_witness + 1)); - - wit -} - pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpression) -> AbiValue { match expression { HirExpression::Tuple(expr_ids) => { @@ -169,38 +115,3 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres _ => unreachable!("Type cannot be used in the abi {:?}", expression), } } - -#[cfg(test)] -mod test { - use std::ops::Range; - - use acvm::acir::native_types::Witness; - - use super::collapse_ranges; - - #[test] - fn collapses_single_range() { - let witnesses: Vec<_> = vec![1, 2, 3].into_iter().map(Witness::from).collect(); - - let collapsed_witnesses = collapse_ranges(&witnesses); - - assert_eq!(collapsed_witnesses, vec![Range { start: Witness(1), end: Witness(4) },]); - } - - #[test] - fn collapse_ranges_correctly() { - let witnesses: Vec<_> = - vec![1, 2, 3, 5, 6, 2, 3, 4].into_iter().map(Witness::from).collect(); - - let collapsed_witnesses = collapse_ranges(&witnesses); - - assert_eq!( - collapsed_witnesses, - vec![ - Range { start: Witness(1), end: Witness(4) }, - Range { start: Witness(5), end: Witness(7) }, - Range { start: Witness(2), end: Witness(5) } - ] - ); - } -} diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 848d9987ca3..4030d017f2b 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -539,17 +539,9 @@ pub fn compile_no_check( info!("Program matches existing artifact, returning early"); return Ok(cached_program.expect("cache must exist for hashes to match")); } - let visibility = program.return_visibility; + let return_visibility = program.return_visibility; - let SsaProgramArtifact { - program, - debug, - warnings, - main_input_witnesses, - main_return_witnesses, - names, - error_types, - } = create_program( + let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = create_program( program, options.show_ssa, options.show_brillig, @@ -557,14 +549,7 @@ pub fn compile_no_check( options.benchmark_codegen, )?; - let abi = abi_gen::gen_abi( - context, - &main_function, - main_input_witnesses, - main_return_witnesses, - visibility, - error_types, - ); + let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); let file_map = filter_relevant_files(&debug, &context.file_manager); Ok(CompiledProgram { diff --git a/docs/docs/getting_started/tooling/noir_codegen.md b/docs/docs/getting_started/tooling/noir_codegen.md index 1c040585340..f7505bef7ab 100644 --- a/docs/docs/getting_started/tooling/noir_codegen.md +++ b/docs/docs/getting_started/tooling/noir_codegen.md @@ -100,7 +100,8 @@ After the export and codegen steps, you should have an `index.ts` like: export type Field = string; -export const is_equal_circuit: CompiledCircuit = {"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; +export const is_equal_circuit: CompiledCircuit = +{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { const program = new Noir(is_equal_circuit); diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index ebcd9ff6569..086dddc27e5 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -205,11 +205,10 @@ fn debug_program_and_decode( let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; let solved_witness = debug_program(&program, &inputs_map)?; - let public_abi = program.abi.public_abi(); match solved_witness { Some(witness) => { - let (_, return_value) = public_abi.decode(&witness)?; + let (_, return_value) = program.abi.decode(&witness)?; Ok((return_value, Some(witness))) } None => Ok((None, None)), diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 5d6754b29f7..b548336275b 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -97,11 +97,10 @@ fn execute_program_and_decode( let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; let witness_stack = execute_program(&program, &inputs_map, foreign_call_resolver_url)?; - let public_abi = program.abi.public_abi(); // Get the entry point witness for the ABI let main_witness = &witness_stack.peek().expect("Should have at least one witness on the stack").witness; - let (_, return_value) = public_abi.decode(main_witness)?; + let (_, return_value) = program.abi.decode(main_witness)?; Ok((return_value, witness_stack)) } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 10fd14a0090..af3011068bf 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -34,7 +34,6 @@ "generate:package": "bash ./fixup.sh", "build": "yarn clean && tsc && tsc -p ./tsconfig.cjs.json && yarn generate:package", "clean": "rm -rf ./lib", - "test": "mocha --timeout 25000 --exit --config ./.mocharc.json", "prettier": "prettier 'src/**/*.ts'", "prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", "nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json", @@ -49,10 +48,8 @@ "devDependencies": { "@types/node": "^20.6.2", "@types/prettier": "^3", - "chai": "^4.4.1", "eslint": "^8.57.0", "eslint-plugin-prettier": "^5.1.3", - "mocha": "^10.2.0", "prettier": "3.2.5", "ts-node": "^10.9.1", "typescript": "5.4.2" diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index f28abb9a658..cefef07520f 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -1,6 +1,5 @@ export { BarretenbergBackend } from './backend.js'; export { BarretenbergVerifier } from './verifier.js'; -export { publicInputsToWitnessMap } from './public_inputs.js'; // typedoc exports export { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; diff --git a/tooling/noir_js_backend_barretenberg/src/public_inputs.ts b/tooling/noir_js_backend_barretenberg/src/public_inputs.ts index 75ee0de6800..ed771ab0d34 100644 --- a/tooling/noir_js_backend_barretenberg/src/public_inputs.ts +++ b/tooling/noir_js_backend_barretenberg/src/public_inputs.ts @@ -1,4 +1,4 @@ -import { Abi, WitnessMap } from '@noir-lang/types'; +import { WitnessMap } from '@noir-lang/types'; export function flattenPublicInputsAsArray(publicInputs: string[]): Uint8Array { const flattenedPublicInputs = publicInputs.map(hexToUint8Array); @@ -23,30 +23,6 @@ export function witnessMapToPublicInputs(publicInputs: WitnessMap): string[] { return flattenedPublicInputs; } -export function publicInputsToWitnessMap(publicInputs: string[], abi: Abi): WitnessMap { - const return_value_witnesses = abi.return_witnesses; - const public_parameters = abi.parameters.filter((param) => param.visibility === 'public'); - const public_parameter_witnesses: number[] = public_parameters.flatMap((param) => - abi.param_witnesses[param.name].flatMap((witness_range) => - Array.from({ length: witness_range.end - witness_range.start }, (_, i) => witness_range.start + i), - ), - ); - - // We now have an array of witness indices which have been deduplicated and sorted in ascending order. - // The elements of this array should correspond to the elements of `flattenedPublicInputs` so that we can build up a `WitnessMap`. - const public_input_witnesses = [...new Set(public_parameter_witnesses.concat(return_value_witnesses))].sort( - (a, b) => a - b, - ); - - const witnessMap: WitnessMap = new Map(); - public_input_witnesses.forEach((witness_index, index) => { - const witness_value = publicInputs[index]; - witnessMap.set(witness_index, witness_value); - }); - - return witnessMap; -} - function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { const totalLength = arrays.reduce((acc, val) => acc + val.length, 0); const result = new Uint8Array(totalLength); diff --git a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts deleted file mode 100644 index cfd43eff250..00000000000 --- a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Abi } from '@noir-lang/types'; -import { expect } from 'chai'; -import { witnessMapToPublicInputs, publicInputsToWitnessMap } from '../src/public_inputs.js'; - -const abi: Abi = { - parameters: [ - { - name: 'array_with_returned_element', - type: { - kind: 'array', - type: { - kind: 'field', - }, - length: 10, - }, - visibility: 'private', - }, - { - name: 'pub_field', - type: { - kind: 'field', - }, - visibility: 'public', - }, - ], - param_witnesses: { - array_with_returned_element: [ - { - start: 1, - end: 11, - }, - ], - pub_field: [ - { - start: 11, - end: 12, - }, - ], - }, - return_type: { - abi_type: { - kind: 'tuple', - fields: [ - { - kind: 'field', - }, - { - kind: 'field', - }, - { - kind: 'field', - }, - ], - }, - visibility: 'public', - }, - return_witnesses: [2, 13, 13], - error_types: {}, -}; - -it('flattens a witness map in order of its witness indices', async () => { - // Note that these are not in ascending order. This means that if we read from `witness_map` in insertion order - // then the witness values will be sorted incorrectly. - const public_input_indices = [2, 13, 11]; - - const witness_map = new Map( - public_input_indices.map((witness_index) => [ - witness_index, - '0x' + BigInt(witness_index).toString(16).padStart(64, '0'), - ]), - ); - - const flattened_public_inputs = witnessMapToPublicInputs(witness_map); - expect(flattened_public_inputs).to.be.deep.eq([ - '0x0000000000000000000000000000000000000000000000000000000000000002', - '0x000000000000000000000000000000000000000000000000000000000000000b', - '0x000000000000000000000000000000000000000000000000000000000000000d', - ]); -}); - -it('recovers the original witness map when deflattening a public input array', async () => { - // Note that these are not in ascending order. This means that if we read from `witness_map` in insertion order - // then the witness values will be sorted incorrectly. - const public_input_indices = [2, 13, 11]; - - const witness_map = new Map( - public_input_indices.map((witness_index) => [ - witness_index, - '0x' + BigInt(witness_index).toString(16).padStart(64, '0'), - ]), - ); - - const flattened_public_inputs = witnessMapToPublicInputs(witness_map); - const deflattened_public_inputs = publicInputsToWitnessMap(flattened_public_inputs, abi); - - expect(deflattened_public_inputs).to.be.deep.eq(witness_map); -}); diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts index 0258f2f90c9..dd6548ddb6c 100644 --- a/tooling/noir_js_types/src/types.ts +++ b/tooling/noir_js_types/src/types.ts @@ -38,9 +38,7 @@ export type WitnessMap = Map; export type Abi = { parameters: AbiParameter[]; - param_witnesses: Record; return_type: { abi_type: AbiType; visibility: Visibility } | null; - return_witnesses: number[]; error_types: Record; }; diff --git a/tooling/noirc_abi/src/input_parser/mod.rs b/tooling/noirc_abi/src/input_parser/mod.rs index 14d92bc71b3..d7bbb0adfe3 100644 --- a/tooling/noirc_abi/src/input_parser/mod.rs +++ b/tooling/noirc_abi/src/input_parser/mod.rs @@ -267,9 +267,6 @@ mod serialization_tests { abi_type: AbiType::String { length: 5 }, visibility: AbiVisibility::Public, }), - // These two fields are unused when serializing/deserializing to file. - param_witnesses: BTreeMap::new(), - return_witnesses: Vec::new(), error_types: Default::default(), }; diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 134dd5e621e..2abb28e2538 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -20,7 +20,7 @@ use noirc_printable_type::{ PrintableValueDisplay, }; use serde::{Deserialize, Serialize}; -use std::{borrow::Borrow, ops::Range}; +use std::borrow::Borrow; use std::{collections::BTreeMap, str}; // This is the ABI used to bridge the different TOML formats for the initial // witness, the partial witness generator and the interpreter. @@ -267,11 +267,7 @@ pub struct AbiReturnType { pub struct Abi { /// An ordered list of the arguments to the program's `main` function, specifying their types and visibility. pub parameters: Vec, - /// A map from the ABI's parameters to the indices they are written to in the [`WitnessMap`]. - /// This defines how to convert between the [`InputMap`] and [`WitnessMap`]. - pub param_witnesses: BTreeMap>>, pub return_type: Option, - pub return_witnesses: Vec, pub error_types: BTreeMap, } @@ -307,25 +303,6 @@ impl Abi { map } - /// ABI with only the public parameters - #[must_use] - pub fn public_abi(self) -> Abi { - let parameters: Vec<_> = - self.parameters.into_iter().filter(|param| param.is_public()).collect(); - let param_witnesses = self - .param_witnesses - .into_iter() - .filter(|(param_name, _)| parameters.iter().any(|param| ¶m.name == param_name)) - .collect(); - Abi { - parameters, - param_witnesses, - return_type: self.return_type, - return_witnesses: self.return_witnesses, - error_types: self.error_types, - } - } - /// Encode a set of inputs as described in the ABI into a `WitnessMap`. pub fn encode( &self, @@ -341,34 +318,21 @@ impl Abi { } // First encode each input separately, performing any input validation. - let encoded_input_map: BTreeMap> = self - .to_btree_map() - .into_iter() - .map(|(param_name, expected_type)| { + let mut encoded_inputs: Vec> = self + .parameters + .iter() + .map(|param| { let value = input_map - .get(¶m_name) - .ok_or_else(|| AbiError::MissingParam(param_name.clone()))? + .get(¶m.name) + .ok_or_else(|| AbiError::MissingParam(param.name.clone()))? .clone(); - value.find_type_mismatch(&expected_type, param_name.clone())?; + value.find_type_mismatch(¶m.typ, param.name.clone())?; - Self::encode_value(value, &expected_type).map(|v| (param_name, v)) + Self::encode_value(value, ¶m.typ) }) .collect::>()?; - // Write input field elements into witness indices specified in `self.param_witnesses`. - let mut witness_map: BTreeMap = encoded_input_map - .iter() - .flat_map(|(param_name, encoded_param_fields)| { - let param_witness_indices = range_to_vec(&self.param_witnesses[param_name]); - param_witness_indices - .iter() - .zip(encoded_param_fields.iter()) - .map(|(&witness, &field_element)| (witness, field_element)) - .collect::>() - }) - .collect::>(); - // When encoding public inputs to be passed to the verifier, the user can must provide a return value // to be inserted into the witness map. This is not needed when generating a witness when proving the circuit. match (&self.return_type, return_value) { @@ -380,18 +344,7 @@ impl Abi { }); } let encoded_return_fields = Self::encode_value(return_value, return_type)?; - - // We need to be more careful when writing the return value's witness values. - // This is as it may share witness indices with other public inputs so we must check that when - // this occurs the witness values are consistent with each other. - self.return_witnesses.iter().zip(encoded_return_fields.iter()).try_for_each( - |(&witness, &field_element)| match witness_map.insert(witness, field_element) { - Some(existing_value) if existing_value != field_element => { - Err(AbiError::InconsistentWitnessAssignment(witness)) - } - _ => Ok(()), - }, - )?; + encoded_inputs.push(encoded_return_fields); } (None, Some(return_value)) => { return Err(AbiError::UnexpectedReturnValue(return_value)) @@ -401,6 +354,14 @@ impl Abi { (_, None) => {} } + // Write input field elements into witness map. + let witness_map: BTreeMap = encoded_inputs + .into_iter() + .flatten() + .enumerate() + .map(|(index, field_element)| (Witness(index as u32), field_element)) + .collect::>(); + Ok(witness_map.into()) } @@ -441,18 +402,21 @@ impl Abi { &self, witness_map: &WitnessMap, ) -> Result<(InputMap, Option), AbiError> { + let mut pointer: u32 = 0; let public_inputs_map = try_btree_map(self.parameters.clone(), |AbiParameter { name, typ, .. }| { - let param_witness_values = - try_vecmap(range_to_vec(&self.param_witnesses[&name]), |witness_index| { - witness_map - .get(&witness_index) - .ok_or_else(|| AbiError::MissingParamWitnessValue { - name: name.clone(), - witness_index, - }) - .copied() - })?; + let num_fields = typ.field_count(); + let param_witness_values = try_vecmap(0..num_fields, |index| { + let witness_index = Witness(pointer + index); + witness_map + .get(&witness_index) + .ok_or_else(|| AbiError::MissingParamWitnessValue { + name: name.clone(), + witness_index, + }) + .copied() + })?; + pointer += num_fields; decode_value(&mut param_witness_values.into_iter(), &typ) .map(|input_value| (name.clone(), input_value)) @@ -461,7 +425,8 @@ impl Abi { // We also attempt to decode the circuit's return value from `witness_map`. let return_value = if let Some(return_type) = &self.return_type { if let Ok(return_witness_values) = - try_vecmap(self.return_witnesses.clone(), |witness_index| { + try_vecmap(0..return_type.abi_type.field_count(), |index| { + let witness_index = Witness(pointer + index); witness_map .get(&witness_index) .ok_or_else(|| AbiError::MissingParamWitnessValue { @@ -587,16 +552,6 @@ pub enum AbiValue { }, } -fn range_to_vec(ranges: &[Range]) -> Vec { - let mut result = Vec::new(); - for range in ranges { - for witness in range.start.witness_index()..range.end.witness_index() { - result.push(witness.into()); - } - } - result -} - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "error_kind", rename_all = "lowercase")] pub enum AbiErrorType { @@ -652,7 +607,7 @@ pub fn display_abi_error( mod test { use std::collections::BTreeMap; - use acvm::{acir::native_types::Witness, AcirField, FieldElement}; + use acvm::{AcirField, FieldElement}; use crate::{ input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, @@ -674,16 +629,10 @@ mod test { visibility: AbiVisibility::Public, }, ], - // Note that the return value shares a witness with `thing2` - param_witnesses: BTreeMap::from([ - ("thing1".to_string(), vec![(Witness(1)..Witness(3))]), - ("thing2".to_string(), vec![(Witness(3)..Witness(4))]), - ]), return_type: Some(AbiReturnType { abi_type: AbiType::Field, visibility: AbiVisibility::Public, }), - return_witnesses: vec![Witness(3)], error_types: BTreeMap::default(), }; @@ -706,7 +655,6 @@ mod test { assert_eq!(reconstructed_inputs[&key], expected_value); } - // We also decode the return value (we can do this immediately as we know it shares a witness with an input). - assert_eq!(return_value.unwrap(), reconstructed_inputs["thing2"]); + assert!(return_value.is_none()); } } diff --git a/yarn.lock b/yarn.lock index 8fb574afa30..a947ec56aba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4400,11 +4400,9 @@ __metadata: "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 - chai: ^4.4.1 eslint: ^8.57.0 eslint-plugin-prettier: ^5.1.3 fflate: ^0.8.0 - mocha: ^10.2.0 prettier: 3.2.5 ts-node: ^10.9.1 typescript: 5.4.2 From 95d0dd514ea7d66219a5c72e2307a2e9e878e421 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 10 Jun 2024 19:17:20 +0100 Subject: [PATCH 11/57] chore: inline `FieldElement.is_negative` and document (#5214) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5174 ## Summary\* This PR inlines `FieldElement.is_negative` into `FieldElement.to_i128` so it can't be misused elsewhere while documenting the reasoning behind it. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/acir_field/src/field_element.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index 81582b9fbac..2c14aefee2a 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -174,10 +174,6 @@ impl FieldElement { self.0 } - fn is_negative(&self) -> bool { - self.neg().num_bits() < self.num_bits() - } - fn fits_in_u128(&self) -> bool { self.num_bits() <= 128 } @@ -278,7 +274,10 @@ impl AcirField for FieldElement { } fn to_i128(self) -> i128 { - let is_negative = self.is_negative(); + // Negative integers are represented by the range [p + i128::MIN, p) whilst + // positive integers are represented by the range [0, i128::MAX). + // We can then differentiate positive from negative values by their MSB. + let is_negative = self.neg().num_bits() < self.num_bits(); let bytes = if is_negative { self.neg() } else { self }.to_be_bytes(); i128::from_be_bytes(bytes[16..32].try_into().unwrap()) * if is_negative { -1 } else { 1 } } From ff7bb7287d70f2e854778553dbb8ca7e8a206ee2 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:03:33 +0100 Subject: [PATCH 12/57] chore: add some property tests to ACVM crates (#5215) # Description ## Problem\* Related to #5173 ## Summary\* This PR adds some usage of the `proptest` crate into ACVM crates to replace some smoke-tests which use hardcoded values. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 5 +-- Cargo.toml | 1 + acvm-repo/acir_field/Cargo.toml | 3 ++ .../proptest-regressions/field_element.txt | 7 +++ acvm-repo/acir_field/src/field_element.rs | 43 ++++++++++++------- acvm-repo/acvm/Cargo.toml | 5 +-- acvm-repo/blackbox_solver/Cargo.toml | 3 ++ acvm-repo/blackbox_solver/src/logic.rs | 32 +++++++++----- cspell.json | 1 + 9 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 acvm-repo/acir_field/proptest-regressions/field_element.txt diff --git a/Cargo.lock b/Cargo.lock index 8e40315cdd7..3480a785d70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,7 @@ dependencies = [ "cfg-if 1.0.0", "hex", "num-bigint", + "proptest", "serde", ] @@ -47,9 +48,6 @@ dependencies = [ "brillig_vm", "indexmap 1.9.3", "num-bigint", - "paste", - "proptest", - "rand 0.8.5", "serde", "thiserror", "tracing", @@ -67,6 +65,7 @@ dependencies = [ "libaes", "num-bigint", "p256", + "proptest", "sha2", "sha3", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index d23089adb56..66a19bf7766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,6 +132,7 @@ tempfile = "3.6.0" jsonrpc = { version = "0.16.0", features = ["minreq_http"] } flate2 = "1.0.24" rand = "0.8.5" +proptest = "1.2.0" im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index f182c6c4aba..15d8aafffef 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -23,6 +23,9 @@ ark-ff = { version = "^0.4.0", default-features = false } cfg-if = "1.0.0" +[dev-dependencies] +proptest.workspace = true + [features] bn254 = [] bls12_381 = ["dep:ark-bls12-381"] diff --git a/acvm-repo/acir_field/proptest-regressions/field_element.txt b/acvm-repo/acir_field/proptest-regressions/field_element.txt new file mode 100644 index 00000000000..79b3209d4f2 --- /dev/null +++ b/acvm-repo/acir_field/proptest-regressions/field_element.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc c1b2755b427c874de3ee55c2969e8adc1b4b0331a28173e9e1e492b3fee6b7b7 # shrinks to hex = "3a0aaaa0aa0aaa00000aaaaaaaa0000a000aaa0a00a0000aa00a00aa0a000a0a" diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index 2c14aefee2a..3c16276adc9 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -425,6 +425,7 @@ fn superscript(n: u64) -> String { #[cfg(test)] mod tests { use super::{AcirField, FieldElement}; + use proptest::prelude::*; #[test] fn serialize_fixed_test_vectors() { @@ -442,24 +443,36 @@ mod tests { } } - #[test] - fn deserialize_even_and_odd_length_hex() { - // Test cases of (odd, even) length hex strings - let hex_strings = - vec![("0x0", "0x00"), ("0x1", "0x01"), ("0x002", "0x0002"), ("0x00003", "0x000003")]; - for (i, case) in hex_strings.into_iter().enumerate() { - let i_field_element = FieldElement::::from(i as i128); - let odd_field_element = FieldElement::::from_hex(case.0).unwrap(); - let even_field_element = FieldElement::::from_hex(case.1).unwrap(); - - assert_eq!(i_field_element, odd_field_element); - assert_eq!(odd_field_element, even_field_element); - } - } - #[test] fn max_num_bits_smoke() { let max_num_bits_bn254 = FieldElement::::max_num_bits(); assert_eq!(max_num_bits_bn254, 254); } + + proptest! { + // This currently panics due to the fact that we allow inputs which are greater than the field modulus, + // automatically reducing them to fit within the canonical range. + #[test] + #[should_panic(expected = "serialized field element is not equal to input")] + fn recovers_original_hex_string(hex in "[0-9a-f]{64}") { + let fe: FieldElement:: = FieldElement::from_hex(&hex).expect("should accept any 32 byte hex string"); + let output_hex = fe.to_hex(); + + prop_assert_eq!(hex, output_hex, "serialized field element is not equal to input"); + } + + #[test] + fn accepts_odd_length_hex_strings(hex in "(?:0x)[0-9a-fA-F]+") { + // Here we inject a "0" immediately after the "0x" (if it exists) to construct an equivalent + // hex string with the opposite parity length. + let insert_index = if hex.starts_with("0x") { 2 } else { 0 }; + let mut opposite_parity_string = hex.to_string(); + opposite_parity_string.insert(insert_index, '0'); + + let fe_1: FieldElement:: = FieldElement::from_hex(&hex).unwrap(); + let fe_2: FieldElement:: = FieldElement::from_hex(&opposite_parity_string).unwrap(); + + prop_assert_eq!(fe_1, fe_2, "equivalent hex strings with opposite parity deserialized to different values"); + } + } } diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index 0bd6ab6b7c7..59305ec49f0 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -37,7 +37,4 @@ bls12_381 = [ ] [dev-dependencies] -rand.workspace = true -proptest = "1.2.0" -paste = "1.0.14" -ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } \ No newline at end of file +ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index 00c87bbca74..dd7f84e63e4 100644 --- a/acvm-repo/blackbox_solver/Cargo.toml +++ b/acvm-repo/blackbox_solver/Cargo.toml @@ -39,6 +39,9 @@ p256 = { version = "0.11.0", features = [ libaes = "0.7.0" +[dev-dependencies] +proptest.workspace = true + [features] bn254 = ["acir/bn254"] bls12_381 = ["acir/bls12_381"] diff --git a/acvm-repo/blackbox_solver/src/logic.rs b/acvm-repo/blackbox_solver/src/logic.rs index 95bd3669ff5..6cccb521c20 100644 --- a/acvm-repo/blackbox_solver/src/logic.rs +++ b/acvm-repo/blackbox_solver/src/logic.rs @@ -68,20 +68,32 @@ fn mask_vector_le(bytes: &mut [u8], num_bits: usize) { #[cfg(test)] mod tests { - use acir::{AcirField, FieldElement}; + use acir::FieldElement; + use proptest::prelude::*; - use crate::bit_and; + use crate::{bit_and, bit_xor}; - #[test] - fn and() { - let max = 10_000u32; + proptest! { + #[test] + fn matches_bitwise_and_on_u128s(x in 0..=u128::MAX, y in 0..=u128::MAX, bit_size in 128u32..) { + let x_as_field = FieldElement::from(x); + let y_as_field = FieldElement::from(y); - let num_bits = (std::mem::size_of::() * 8) as u32 - max.leading_zeros(); + let x_and_y = x & y; + let x_and_y_as_field = bit_and(x_as_field, y_as_field, bit_size); - for x in 0..max { - let x = FieldElement::from(x as i128); - let res = bit_and(x, x, num_bits); - assert_eq!(res.to_be_bytes(), x.to_be_bytes()); + prop_assert_eq!(x_and_y_as_field, FieldElement::from(x_and_y), "AND on fields should match that on integers"); + } + + #[test] + fn matches_bitwise_xor_on_u128s(x in 0..=u128::MAX, y in 0..=u128::MAX, bit_size in 128u32..) { + let x_as_field = FieldElement::from(x); + let y_as_field = FieldElement::from(y); + + let x_xor_y = x ^ y; + let x_xor_y_as_field = bit_xor(x_as_field, y_as_field, bit_size); + + prop_assert_eq!(x_xor_y_as_field, FieldElement::from(x_xor_y), "XOR on fields should match that on integers"); } } } diff --git a/cspell.json b/cspell.json index cea0dd7ed38..9eb6e6f9239 100644 --- a/cspell.json +++ b/cspell.json @@ -157,6 +157,7 @@ "preprocess", "prettytable", "printstd", + "proptest", "pseudocode", "pubkey", "quantile", From ff67e145d086bf6fdf58fb5e57927033e52e03d3 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 11 Jun 2024 09:49:04 -0400 Subject: [PATCH 13/57] feat: Sync from aztec-packages (#5222) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE chore: add example for recursion on the CLI (https://github.com/AztecProtocol/aztec-packages/pull/6389) feat(nargo): hidden option to show contract artifact paths written by `nargo compile` (https://github.com/AztecProtocol/aztec-packages/pull/6131) chore: add bench programs (https://github.com/AztecProtocol/aztec-packages/pull/6566) END_COMMIT_OVERRIDE --------- Co-authored-by: TomAFrench Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .aztec-sync-commit | 2 +- acvm-repo/acvm_js/build.sh | 2 +- aztec_macros/src/transforms/functions.rs | 34 ++++-- .../test/node/prove_and_verify.test.ts | 12 +-- compiler/noirc_driver/src/lib.rs | 4 + examples/recursion/.gitignore | 2 + examples/recursion/Nargo.toml | 2 + .../recursion/generate_recursive_proof.sh | 61 +++++++++++ examples/recursion/recurse_leaf/Nargo.toml | 7 ++ examples/recursion/recurse_leaf/src/main.nr | 20 ++++ examples/recursion/recurse_node/Nargo.toml | 7 ++ examples/recursion/recurse_node/src/main.nr | 17 +++ examples/recursion/sum/Nargo.toml | 7 ++ examples/recursion/sum/Prover.toml | 2 + examples/recursion/sum/src/main.nr | 4 + examples/recursion/test.sh | 8 ++ .../bench_eddsa_poseidon/Nargo.toml | 7 ++ .../bench_eddsa_poseidon/Prover.toml | 6 ++ .../bench_eddsa_poseidon/src/main.nr | 12 +++ .../benchmarks/bench_poseidon_hash/Nargo.toml | 7 ++ .../bench_poseidon_hash/Prover.toml | 1 + .../bench_poseidon_hash/src/main.nr | 5 + .../bench_poseidon_hash_100/Nargo.toml | 7 ++ .../bench_poseidon_hash_100/Prover.toml | 102 ++++++++++++++++++ .../bench_poseidon_hash_100/src/main.nr | 12 +++ .../bench_poseidon_hash_30/Nargo.toml | 7 ++ .../bench_poseidon_hash_30/Prover.toml | 32 ++++++ .../bench_poseidon_hash_30/src/main.nr | 12 +++ .../benchmarks/bench_sha256/Nargo.toml | 7 ++ .../benchmarks/bench_sha256/Prover.toml | 1 + .../benchmarks/bench_sha256/src/main.nr | 5 + .../benchmarks/bench_sha256_100/Nargo.toml | 7 ++ .../benchmarks/bench_sha256_100/Prover.toml | 102 ++++++++++++++++++ .../benchmarks/bench_sha256_100/src/main.nr | 12 +++ .../benchmarks/bench_sha256_30/Nargo.toml | 7 ++ .../benchmarks/bench_sha256_30/Prover.toml | 32 ++++++ .../benchmarks/bench_sha256_30/src/main.nr | 12 +++ test_programs/rebuild.sh | 7 ++ tooling/debugger/src/context.rs | 4 +- tooling/nargo_cli/src/cli/compile_cmd.rs | 14 ++- 40 files changed, 588 insertions(+), 23 deletions(-) create mode 100644 examples/recursion/.gitignore create mode 100644 examples/recursion/Nargo.toml create mode 100755 examples/recursion/generate_recursive_proof.sh create mode 100644 examples/recursion/recurse_leaf/Nargo.toml create mode 100644 examples/recursion/recurse_leaf/src/main.nr create mode 100644 examples/recursion/recurse_node/Nargo.toml create mode 100644 examples/recursion/recurse_node/src/main.nr create mode 100644 examples/recursion/sum/Nargo.toml create mode 100644 examples/recursion/sum/Prover.toml create mode 100644 examples/recursion/sum/src/main.nr create mode 100755 examples/recursion/test.sh create mode 100644 test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml create mode 100644 test_programs/benchmarks/bench_eddsa_poseidon/Prover.toml create mode 100644 test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr create mode 100644 test_programs/benchmarks/bench_poseidon_hash/Nargo.toml create mode 100644 test_programs/benchmarks/bench_poseidon_hash/Prover.toml create mode 100644 test_programs/benchmarks/bench_poseidon_hash/src/main.nr create mode 100644 test_programs/benchmarks/bench_poseidon_hash_100/Nargo.toml create mode 100644 test_programs/benchmarks/bench_poseidon_hash_100/Prover.toml create mode 100644 test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr create mode 100644 test_programs/benchmarks/bench_poseidon_hash_30/Nargo.toml create mode 100644 test_programs/benchmarks/bench_poseidon_hash_30/Prover.toml create mode 100644 test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr create mode 100644 test_programs/benchmarks/bench_sha256/Nargo.toml create mode 100644 test_programs/benchmarks/bench_sha256/Prover.toml create mode 100644 test_programs/benchmarks/bench_sha256/src/main.nr create mode 100644 test_programs/benchmarks/bench_sha256_100/Nargo.toml create mode 100644 test_programs/benchmarks/bench_sha256_100/Prover.toml create mode 100644 test_programs/benchmarks/bench_sha256_100/src/main.nr create mode 100644 test_programs/benchmarks/bench_sha256_30/Nargo.toml create mode 100644 test_programs/benchmarks/bench_sha256_30/Prover.toml create mode 100644 test_programs/benchmarks/bench_sha256_30/src/main.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index fdbc14d511e..0bdea76121d 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -1d785fd1087d7387fc29213ca3be50b2fc9c4725 +86a33140f9a65e518003b3f4c60f97d132f85b89 diff --git a/acvm-repo/acvm_js/build.sh b/acvm-repo/acvm_js/build.sh index ee93413ab85..16fb26e55db 100755 --- a/acvm-repo/acvm_js/build.sh +++ b/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -# require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index 0fb13975fec..4d8b6ef7cdf 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -216,12 +216,30 @@ pub fn export_fn_abi( /// /// Inserts the following code at the beginning of an unconstrained function /// ```noir -/// let storage = Storage::init(Context::none()); +/// let context = UnconstrainedContext::new(); +/// let storage = Storage::init(context); /// ``` /// /// This will allow developers to access their contract' storage struct in unconstrained functions pub fn transform_unconstrained(func: &mut NoirFunction, storage_struct_name: String) { + // let context = UnconstrainedContext::new(); + let let_context = assignment( + "context", // Assigned to + call( + variable_path(chained_dep!( + "aztec", + "context", + "unconstrained_context", + "UnconstrainedContext", + "new" + )), + vec![], + ), + ); + + // We inject the statements at the beginning, in reverse order. func.def.body.statements.insert(0, abstract_storage(storage_struct_name, true)); + func.def.body.statements.insert(0, let_context); } /// Helper function that returns what the private context would look like in the ast @@ -597,7 +615,7 @@ fn abstract_return_values(func: &NoirFunction) -> Result>, /// ```noir /// #[aztec(private)] /// fn lol() { -/// let storage = Storage::init(context); +/// let storage = Storage::init(&mut context); /// } /// ``` /// @@ -605,22 +623,18 @@ fn abstract_return_values(func: &NoirFunction) -> Result>, /// ```noir /// #[aztec(public)] /// fn lol() { -/// let storage = Storage::init(context); +/// let storage = Storage::init(&mut context); /// } /// ``` /// /// For unconstrained functions: /// ```noir /// unconstrained fn lol() { -/// let storage = Storage::init(()); +/// let storage = Storage::init(context); /// } fn abstract_storage(storage_struct_name: String, unconstrained: bool) -> Statement { - let context_expr = if unconstrained { - // Note that the literal unit type (i.e. '()') is not the same as a tuple with zero elements - expression(ExpressionKind::Literal(Literal::Unit)) - } else { - mutable_reference("context") - }; + let context_expr = + if unconstrained { variable("context") } else { mutable_reference("context") }; assignment( "storage", // Assigned to diff --git a/compiler/integration-tests/test/node/prove_and_verify.test.ts b/compiler/integration-tests/test/node/prove_and_verify.test.ts index 1cfe36f31e9..15d39436a3b 100644 --- a/compiler/integration-tests/test/node/prove_and_verify.test.ts +++ b/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -42,10 +42,10 @@ it('end-to-end proof creation and verification (outer) -- Verifier API', async ( const { witness } = await program.execute(inputs); // Generate proof - const backend = new Backend(assert_lt_program); - const proof = await backend.generateProof(witness); + const prover = new Backend(assert_lt_program); + const proof = await prover.generateProof(witness); - const verificationKey = await backend.getVerificationKey(); + const verificationKey = await prover.getVerificationKey(); // Proof verification const verifier = new Verifier(); @@ -147,10 +147,10 @@ it('end-to-end proof creation and verification for multiple ACIR circuits (inner // bb.js part // // Proof creation - const backend = new Backend(fold_fibonacci_program); - const proof = await backend.generateProof(witness); + const prover = new Backend(fold_fibonacci_program); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await backend.verifyProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 4030d017f2b..f8043a60f8c 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -102,6 +102,10 @@ pub struct CompileOptions { /// Enable the experimental elaborator pass #[arg(long, hide = true)] pub use_elaborator: bool, + + /// Outputs the paths to any modified artifacts + #[arg(long, hide = true)] + pub show_artifact_paths: bool, } fn parse_expression_width(input: &str) -> Result { diff --git a/examples/recursion/.gitignore b/examples/recursion/.gitignore new file mode 100644 index 00000000000..12587094b0f --- /dev/null +++ b/examples/recursion/.gitignore @@ -0,0 +1,2 @@ +recurse_leaf/Prover.toml +recurse_node/Prover.toml \ No newline at end of file diff --git a/examples/recursion/Nargo.toml b/examples/recursion/Nargo.toml new file mode 100644 index 00000000000..7c7a8b1926f --- /dev/null +++ b/examples/recursion/Nargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["recurse_leaf", "recurse_node", "sum"] diff --git a/examples/recursion/generate_recursive_proof.sh b/examples/recursion/generate_recursive_proof.sh new file mode 100755 index 00000000000..362512529d4 --- /dev/null +++ b/examples/recursion/generate_recursive_proof.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -eu + +BACKEND=${BACKEND:-bb} + +nargo execute sum_witness --package sum +$BACKEND prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof + +# Once we have generated our inner proof, we must use this to generate inputs to `recurse_leaf`` + +$BACKEND write_vk -b ./target/sum.json -o ./target/sum_key +$BACKEND vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields +VK_HASH=$(jq -r '.[0]' ./target/sum_vk_as_fields) +VK_AS_FIELDS=$(jq -r '.[1:]' ./target/sum_vk_as_fields) + +FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" +# sum has 3 public inputs +PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:3]') +PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[3:]') + +RECURSE_LEAF_PROVER_TOML=./recurse_leaf/Prover.toml +echo "num = 2" > $RECURSE_LEAF_PROVER_TOML +echo "key_hash = \"$VK_HASH\"" >> $RECURSE_LEAF_PROVER_TOML +echo "verification_key = $VK_AS_FIELDS" >> $RECURSE_LEAF_PROVER_TOML +echo "proof = $PROOF_AS_FIELDS" >> $RECURSE_LEAF_PROVER_TOML +echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_LEAF_PROVER_TOML + +# We can now execute and prove `recurse_leaf` + +nargo execute recurse_leaf_witness --package recurse_leaf +$BACKEND prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof + +# Let's do a sanity check that the proof we've generated so far is valid. +$BACKEND write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key +$BACKEND verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key + +# Now we generate the final `recurse_node` proof similarly to how we did for `recurse_leaf`. + +$BACKEND vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields +VK_HASH=$(jq -r '.[0]' ./target/recurse_leaf_vk_as_fields) +VK_AS_FIELDS=$(jq -r '.[1:]' ./target/recurse_leaf_vk_as_fields) + +FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" +# recurse_leaf has 4 public inputs (excluding aggregation object) +PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:4]') +PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[4:]') + +RECURSE_NODE_PROVER_TOML=./recurse_node/Prover.toml +echo "key_hash = \"$VK_HASH\"" > $RECURSE_NODE_PROVER_TOML +echo "verification_key = $VK_AS_FIELDS" >> $RECURSE_NODE_PROVER_TOML +echo "proof = $PROOF_AS_FIELDS" >> $RECURSE_NODE_PROVER_TOML +echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_NODE_PROVER_TOML + +# We can now execute and prove `recurse_node` + +nargo execute recurse_node_witness --package recurse_node +$BACKEND prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof + +# We finally verify that the generated recursive proof is valid. +$BACKEND write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key +$BACKEND verify -p ./target/recurse_node_proof -k ./target/recurse_node_key diff --git a/examples/recursion/recurse_leaf/Nargo.toml b/examples/recursion/recurse_leaf/Nargo.toml new file mode 100644 index 00000000000..7af9cd74940 --- /dev/null +++ b/examples/recursion/recurse_leaf/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "recurse_leaf" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/examples/recursion/recurse_leaf/src/main.nr b/examples/recursion/recurse_leaf/src/main.nr new file mode 100644 index 00000000000..b6a2b49b219 --- /dev/null +++ b/examples/recursion/recurse_leaf/src/main.nr @@ -0,0 +1,20 @@ +use dep::std; + +#[recursive] +fn main( + verification_key: [Field; 114], + public_inputs: pub [Field; 3], + key_hash: Field, + proof: [Field; 93], + num: u64 +) -> pub u64 { + // verify sum so far was computed correctly + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + // Take output of previous proof and add another number to it. + public_inputs[2] as u64 + num +} \ No newline at end of file diff --git a/examples/recursion/recurse_node/Nargo.toml b/examples/recursion/recurse_node/Nargo.toml new file mode 100644 index 00000000000..db4b3b77693 --- /dev/null +++ b/examples/recursion/recurse_node/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "recurse_node" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/examples/recursion/recurse_node/src/main.nr b/examples/recursion/recurse_node/src/main.nr new file mode 100644 index 00000000000..7c983dcf050 --- /dev/null +++ b/examples/recursion/recurse_node/src/main.nr @@ -0,0 +1,17 @@ +use dep::std; + +fn main( + verification_key: [Field; 114], + public_inputs: pub [Field; 4], + key_hash: Field, + proof: [Field; 109] +) -> pub u64 { + // verify sum was computed correctly + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + public_inputs[3] as u64 +} \ No newline at end of file diff --git a/examples/recursion/sum/Nargo.toml b/examples/recursion/sum/Nargo.toml new file mode 100644 index 00000000000..7a5f1498c3e --- /dev/null +++ b/examples/recursion/sum/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sum" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/examples/recursion/sum/Prover.toml b/examples/recursion/sum/Prover.toml new file mode 100644 index 00000000000..eae66cb8593 --- /dev/null +++ b/examples/recursion/sum/Prover.toml @@ -0,0 +1,2 @@ +a = 1 +b = 2 \ No newline at end of file diff --git a/examples/recursion/sum/src/main.nr b/examples/recursion/sum/src/main.nr new file mode 100644 index 00000000000..722d941d57d --- /dev/null +++ b/examples/recursion/sum/src/main.nr @@ -0,0 +1,4 @@ +#[recursive] +fn main(a: pub u64, b: pub u64) -> pub u64 { + a + b +} \ No newline at end of file diff --git a/examples/recursion/test.sh b/examples/recursion/test.sh new file mode 100755 index 00000000000..1e9eca35cab --- /dev/null +++ b/examples/recursion/test.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -eu + +# This file is used for Noir CI and is not required. + +BACKEND=${BACKEND:-bb} + +./generate_recursive_proof.sh \ No newline at end of file diff --git a/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml b/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml new file mode 100644 index 00000000000..bc2a779f7b2 --- /dev/null +++ b/test_programs/benchmarks/bench_eddsa_poseidon/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_eddsa_poseidon" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_eddsa_poseidon/Prover.toml b/test_programs/benchmarks/bench_eddsa_poseidon/Prover.toml new file mode 100644 index 00000000000..8cfb63cd80e --- /dev/null +++ b/test_programs/benchmarks/bench_eddsa_poseidon/Prover.toml @@ -0,0 +1,6 @@ +msg = 789 +pub_key_x = "0x16b051f37589e0dcf4ad3c415c090798c10d3095bedeedabfcc709ad787f3507" +pub_key_y = "0x062800ac9e60839fab9218e5ed9d541f4586e41275f4071816a975895d349a5e" +r8_x = "0x163814666f04c4d2969059a6b63ee26a0f9f0f81bd5957b0796e2e8f4a8a2f06" +r8_y = "0x1255b17d9e4bfb81831625b788f8a1665128079ac4b6c8c3cd1b857666a05a54" +s = "1230930278088778318663840827871215383007447616379808164955640681455510074924" \ No newline at end of file diff --git a/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr b/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr new file mode 100644 index 00000000000..31c2f1f2d13 --- /dev/null +++ b/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr @@ -0,0 +1,12 @@ +use dep::std::eddsa::{eddsa_poseidon_verify}; + +fn main( + msg: pub Field, + pub_key_x: Field, + pub_key_y: Field, + r8_x: Field, + r8_y: Field, + s: Field +) -> pub bool { + eddsa_poseidon_verify(pub_key_x, pub_key_y, s, r8_x, r8_y, msg) +} \ No newline at end of file diff --git a/test_programs/benchmarks/bench_poseidon_hash/Nargo.toml b/test_programs/benchmarks/bench_poseidon_hash/Nargo.toml new file mode 100644 index 00000000000..9024d9a7301 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_poseidon_hash" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_poseidon_hash/Prover.toml b/test_programs/benchmarks/bench_poseidon_hash/Prover.toml new file mode 100644 index 00000000000..66779dea9d7 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash/Prover.toml @@ -0,0 +1 @@ +input = [1,2] diff --git a/test_programs/benchmarks/bench_poseidon_hash/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash/src/main.nr new file mode 100644 index 00000000000..38adeef6ec7 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash/src/main.nr @@ -0,0 +1,5 @@ +use dep::std::hash::poseidon; + +fn main(input: [Field; 2]) -> pub Field { + poseidon::bn254::hash_2(input) +} diff --git a/test_programs/benchmarks/bench_poseidon_hash_100/Nargo.toml b/test_programs/benchmarks/bench_poseidon_hash_100/Nargo.toml new file mode 100644 index 00000000000..b23716b2a20 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash_100/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_poseidon_hash_100" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_poseidon_hash_100/Prover.toml b/test_programs/benchmarks/bench_poseidon_hash_100/Prover.toml new file mode 100644 index 00000000000..542b4a08dd6 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash_100/Prover.toml @@ -0,0 +1,102 @@ +input = [ + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], +] \ No newline at end of file diff --git a/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr new file mode 100644 index 00000000000..fc9a5b7a970 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr @@ -0,0 +1,12 @@ +use dep::std::hash; + +global SIZE = 100; + +fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { + let mut results: [Field; SIZE] = [0; SIZE]; + for i in 0..SIZE { + results[i] = hash::poseidon::bn254::hash_2(input[i]); + } + + results +} \ No newline at end of file diff --git a/test_programs/benchmarks/bench_poseidon_hash_30/Nargo.toml b/test_programs/benchmarks/bench_poseidon_hash_30/Nargo.toml new file mode 100644 index 00000000000..dbcbc07b1ba --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash_30/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_poseidon_hash_30" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_poseidon_hash_30/Prover.toml b/test_programs/benchmarks/bench_poseidon_hash_30/Prover.toml new file mode 100644 index 00000000000..7792a9ab8e3 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash_30/Prover.toml @@ -0,0 +1,32 @@ +input = [ + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], +] \ No newline at end of file diff --git a/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr new file mode 100644 index 00000000000..4d2d94e4946 --- /dev/null +++ b/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr @@ -0,0 +1,12 @@ +use dep::std::hash; + +global SIZE = 30; + +fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { + let mut results: [Field; SIZE] = [0; SIZE]; + for i in 0..SIZE { + results[i] = hash::poseidon::bn254::hash_2(input[i]); + } + + results +} \ No newline at end of file diff --git a/test_programs/benchmarks/bench_sha256/Nargo.toml b/test_programs/benchmarks/bench_sha256/Nargo.toml new file mode 100644 index 00000000000..488b94ca858 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_sha256" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_sha256/Prover.toml b/test_programs/benchmarks/bench_sha256/Prover.toml new file mode 100644 index 00000000000..66779dea9d7 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256/Prover.toml @@ -0,0 +1 @@ +input = [1,2] diff --git a/test_programs/benchmarks/bench_sha256/src/main.nr b/test_programs/benchmarks/bench_sha256/src/main.nr new file mode 100644 index 00000000000..fc873fb4afb --- /dev/null +++ b/test_programs/benchmarks/bench_sha256/src/main.nr @@ -0,0 +1,5 @@ +use dep::std; + +fn main(input: [u8; 2]) -> pub [u8; 32] { + std::hash::sha256(input) +} \ No newline at end of file diff --git a/test_programs/benchmarks/bench_sha256_100/Nargo.toml b/test_programs/benchmarks/bench_sha256_100/Nargo.toml new file mode 100644 index 00000000000..d0c90d75088 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256_100/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_sha256_100" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_sha256_100/Prover.toml b/test_programs/benchmarks/bench_sha256_100/Prover.toml new file mode 100644 index 00000000000..542b4a08dd6 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256_100/Prover.toml @@ -0,0 +1,102 @@ +input = [ + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], +] \ No newline at end of file diff --git a/test_programs/benchmarks/bench_sha256_100/src/main.nr b/test_programs/benchmarks/bench_sha256_100/src/main.nr new file mode 100644 index 00000000000..d78ca8002d2 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256_100/src/main.nr @@ -0,0 +1,12 @@ +use dep::std; + +global SIZE = 100; + +fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { + let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; + for i in 0..SIZE { + results[i] = std::hash::sha256(input[i]); + } + + results +} \ No newline at end of file diff --git a/test_programs/benchmarks/bench_sha256_30/Nargo.toml b/test_programs/benchmarks/bench_sha256_30/Nargo.toml new file mode 100644 index 00000000000..c1dc76df394 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256_30/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench_sha256_30" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/benchmarks/bench_sha256_30/Prover.toml b/test_programs/benchmarks/bench_sha256_30/Prover.toml new file mode 100644 index 00000000000..7792a9ab8e3 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256_30/Prover.toml @@ -0,0 +1,32 @@ +input = [ + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], + [1,2], +] \ No newline at end of file diff --git a/test_programs/benchmarks/bench_sha256_30/src/main.nr b/test_programs/benchmarks/bench_sha256_30/src/main.nr new file mode 100644 index 00000000000..fa66d626586 --- /dev/null +++ b/test_programs/benchmarks/bench_sha256_30/src/main.nr @@ -0,0 +1,12 @@ +use dep::std; + +global SIZE = 30; + +fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { + let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; + for i in 0..SIZE { + results[i] = std::hash::sha256(input[i]); + } + + results +} \ No newline at end of file diff --git a/test_programs/rebuild.sh b/test_programs/rebuild.sh index cfc6479febf..2d4147cb08c 100755 --- a/test_programs/rebuild.sh +++ b/test_programs/rebuild.sh @@ -55,6 +55,13 @@ for dir in $base_path/*; do dirs_to_process+=("$dir") done +for dir in $current_dir/benchmarks/*; do + if [[ ! -d $dir ]]; then + continue + fi + dirs_to_process+=("$dir") +done + # Process each directory in parallel pids=() if [ -z $NO_PARALLEL ]; then diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 5d0916b4863..4436ce0ec3d 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -296,7 +296,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.handle_foreign_call(foreign_call) } Err(err) => DebugCommandResult::Error(NargoError::ExecutionError( - // TODO: debugger does not not handle multiple acir calls + // TODO: debugger does not handle multiple acir calls ExecutionError::SolvingError(err, None), )), } @@ -340,7 +340,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } ACVMStatus::Failure(error) => DebugCommandResult::Error(NargoError::ExecutionError( - // TODO: debugger does not not handle multiple acir calls + // TODO: debugger does not handle multiple acir calls ExecutionError::SolvingError(error, None), )), ACVMStatus::RequiresForeignCall(_) => { diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index ecf2e2e9f53..bd76cf24805 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -142,7 +142,7 @@ pub(super) fn compile_workspace_full( let circuit_dir = workspace.target_directory_path(); for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { let contract = nargo::ops::transform_contract(contract, compile_options.expression_width); - save_contract(contract, &package, &circuit_dir); + save_contract(contract, &package, &circuit_dir, compile_options.show_artifact_paths); } Ok(()) @@ -200,11 +200,19 @@ pub(super) fn save_program(program: CompiledProgram, package: &Package, circuit_ save_program_to_file(&program_artifact, &package.name, circuit_dir); } -fn save_contract(contract: CompiledContract, package: &Package, circuit_dir: &Path) { +fn save_contract( + contract: CompiledContract, + package: &Package, + circuit_dir: &Path, + show_artifact_paths: bool, +) { let contract_name = contract.name.clone(); - save_contract_to_file( + let artifact_path = save_contract_to_file( &contract.into(), &format!("{}-{}", package.name, contract_name), circuit_dir, ); + if show_artifact_paths { + println!("Saved contract artifact to: {}", artifact_path.display()); + } } From 1d62c59a8f02f7d277c5bf9ed637348a3b2f399c Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:55:07 +0100 Subject: [PATCH 14/57] chore!: remove `distinct` keyword (#5219) # Description ## Problem\* Resolves ## Summary\* This PR removes the `distinct` keyword and any traces of distinctiveness from the codebase as they have been deprecated for a couple of releases. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/ast/mod.rs | 23 ------------------- .../src/hir/resolution/errors.rs | 14 ----------- compiler/noirc_frontend/src/lexer/token.rs | 3 --- .../noirc_frontend/src/noir_parser.lalrpop | 1 - compiler/noirc_frontend/src/parser/errors.rs | 2 -- .../src/parser/parser/function.rs | 23 +++---------------- tooling/noirc_abi/src/lib.rs | 15 ------------ 7 files changed, 3 insertions(+), 78 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 39b23228967..bd2b45d9c48 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -390,26 +390,3 @@ impl std::fmt::Display for Visibility { } } } - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -/// Represents whether the return value should compromise of unique witness indices such that no -/// index occurs within the program's abi more than once. -/// -/// This is useful for application stacks that require an uniform abi across across multiple -/// circuits. When index duplication is allowed, the compiler may identify that a public input -/// reaches the output unaltered and is thus referenced directly, causing the input and output -/// witness indices to overlap. Similarly, repetitions of copied values in the output may be -/// optimized away. -pub enum Distinctness { - Distinct, - DuplicationAllowed, -} - -impl std::fmt::Display for Distinctness { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Distinct => write!(f, "distinct"), - Self::DuplicationAllowed => write!(f, "duplication-allowed"), - } - } -} diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index d6dd1c2cd54..06f6dda7142 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -40,8 +40,6 @@ pub enum ResolverError { UnnecessaryPub { ident: Ident, position: PubPosition }, #[error("Required 'pub', main function must return public value")] NecessaryPub { ident: Ident }, - #[error("'distinct' keyword can only be used with main method")] - DistinctNotAllowed { ident: Ident }, #[error("Missing expression for declared constant")] MissingRhsExpr { name: String, span: Span }, #[error("Expression invalid in an array length context")] @@ -220,18 +218,6 @@ impl<'a> From<&'a ResolverError> for Diagnostic { diag.add_note("The `pub` keyword is mandatory for the entry-point function return type because the verifier cannot retrieve private witness and thus the function will not be able to return a 'priv' value".to_owned()); diag } - ResolverError::DistinctNotAllowed { ident } => { - let name = &ident.0.contents; - - let mut diag = Diagnostic::simple_error( - format!("Invalid `distinct` keyword on return type of function {name}"), - "Invalid distinct on return type".to_string(), - ident.0.span(), - ); - - diag.add_note("The `distinct` keyword is only valid when used on the main function of a program, as its only purpose is to ensure that all witness indices that occur in the abi are unique".to_owned()); - diag - } ResolverError::MissingRhsExpr { name, span } => Diagnostic::simple_error( format!( "no expression specifying the value stored by the constant variable {name}" diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 950697c2536..d8555b4fbf7 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -839,7 +839,6 @@ pub enum Keyword { Contract, Crate, Dep, - Distinct, Else, Field, Fn, @@ -884,7 +883,6 @@ impl fmt::Display for Keyword { Keyword::Contract => write!(f, "contract"), Keyword::Crate => write!(f, "crate"), Keyword::Dep => write!(f, "dep"), - Keyword::Distinct => write!(f, "distinct"), Keyword::Else => write!(f, "else"), Keyword::Field => write!(f, "Field"), Keyword::Fn => write!(f, "fn"), @@ -932,7 +930,6 @@ impl Keyword { "contract" => Keyword::Contract, "crate" => Keyword::Crate, "dep" => Keyword::Dep, - "distinct" => Keyword::Distinct, "else" => Keyword::Else, "Field" => Keyword::Field, "fn" => Keyword::Fn, diff --git a/compiler/noirc_frontend/src/noir_parser.lalrpop b/compiler/noirc_frontend/src/noir_parser.lalrpop index 9acb5ef8b58..c6cb788a5a4 100644 --- a/compiler/noirc_frontend/src/noir_parser.lalrpop +++ b/compiler/noirc_frontend/src/noir_parser.lalrpop @@ -67,7 +67,6 @@ extern { "contract" => BorrowedToken::Keyword(noir_token::Keyword::Contract), "crate" => BorrowedToken::Keyword(noir_token::Keyword::Crate), "dep" => BorrowedToken::Keyword(noir_token::Keyword::Dep), - "distinct" => BorrowedToken::Keyword(noir_token::Keyword::Distinct), "else" => BorrowedToken::Keyword(noir_token::Keyword::Else), "Field" => BorrowedToken::Keyword(noir_token::Keyword::Field), "fn" => BorrowedToken::Keyword(noir_token::Keyword::Fn), diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 9f9a8200954..af3d4caa145 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -30,8 +30,6 @@ pub enum ParserErrorReason { TraitImplFunctionModifiers, #[error("comptime keyword is deprecated")] ComptimeDeprecated, - #[error("distinct keyword is deprecated. The `distinct` behavior is now the default.")] - DistinctDeprecated, #[error("{0} are experimental and aren't fully supported yet")] ExperimentalFeature(&'static str), #[error( diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 40180a9f9ac..4db5637f6a7 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -4,16 +4,12 @@ use super::{ parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, self_parameter, where_clause, NoirParser, }; +use crate::ast::{ + FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, Visibility, +}; use crate::parser::labels::ParsingRuleLabel; use crate::parser::spanned; use crate::token::{Keyword, Token}; -use crate::{ - ast::{ - FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, - Visibility, - }, - parser::{ParserError, ParserErrorReason}, -}; use chumsky::prelude::*; @@ -95,20 +91,9 @@ pub(super) fn generics() -> impl NoirParser> { .map(|opt| opt.unwrap_or_default()) } -#[deprecated = "Distinct keyword is now deprecated. Remove this function after the 0.30.0 release"] -fn optional_distinctness() -> impl NoirParser { - keyword(Keyword::Distinct).or_not().validate(|opt, span, emit| { - if opt.is_some() { - emit(ParserError::with_reason(ParserErrorReason::DistinctDeprecated, span)); - } - opt.is_some() - }) -} - pub(super) fn function_return_type() -> impl NoirParser<(Visibility, FunctionReturnType)> { #[allow(deprecated)] just(Token::Arrow) - .ignore_then(optional_distinctness()) .ignore_then(optional_visibility()) .then(spanned(parse_type())) .or_not() @@ -224,8 +209,6 @@ mod test { // A leading plus is not allowed. "fn func_name(f: Field, y : T) where T: + SomeTrait {}", "fn func_name(f: Field, y : T) where T: TraitX + {}", - // `distinct` is deprecated - "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", ], ); } diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 2abb28e2538..514fac2e73d 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -107,21 +107,6 @@ impl From<&Visibility> for AbiVisibility { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -/// Represents whether the return value should compromise of unique witness indices such that no -/// index occurs within the program's abi more than once. -/// -/// This is useful for application stacks that require an uniform abi across across multiple -/// circuits. When index duplication is allowed, the compiler may identify that a public input -/// reaches the output unaltered and is thus referenced directly, causing the input and output -/// witness indices to overlap. Similarly, repetitions of copied values in the output may be -/// optimized away. -pub enum AbiDistinctness { - Distinct, - DuplicationAllowed, -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Sign { From 2e543b40eb83ef2080e4d8f870f525fadd631099 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:31:43 +0100 Subject: [PATCH 15/57] chore: avoid `bn254_blackbox_solver` polluting feature flags (#5141) # Description ## Problem\* Resolves ## Summary\* Depending on `bn254_blackbox_solver` results in the `bn254` feature flag being applied to the `acir` crate. This is unnecessary as we can just have this crate use a `GenericFieldElement` and construct it as a BN254 field. This simplifies the lives of people attempting to use Noir with an alternative field choice. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 1 + Cargo.toml | 8 ++++++++ acvm-repo/acir_field/Cargo.toml | 6 +++--- acvm-repo/bn254_blackbox_solver/Cargo.toml | 14 +++++--------- .../src/embedded_curve_ops.rs | 3 ++- acvm-repo/bn254_blackbox_solver/src/lib.rs | 5 ++++- .../src/pedersen/commitment.rs | 3 ++- .../bn254_blackbox_solver/src/pedersen/hash.rs | 3 ++- acvm-repo/bn254_blackbox_solver/src/poseidon2.rs | 8 +++++--- acvm-repo/bn254_blackbox_solver/src/schnorr/mod.rs | 3 ++- 10 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3480a785d70..bd575d0dca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,6 +574,7 @@ version = "0.46.0" dependencies = [ "acir", "acvm_blackbox_solver", + "ark-bn254", "ark-ec", "ark-ff", "ark-std", diff --git a/Cargo.toml b/Cargo.toml index 66a19bf7766..c08e9f9f38b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,14 @@ noirc_abi = { path = "tooling/noirc_abi" } bb_abstraction_leaks = { path = "tooling/bb_abstraction_leaks" } acvm_cli = { path = "tooling/acvm_cli" } +# Arkworks +ark-bn254 = { version = "^0.4.0", default-features = false, features = ["curve"] } +ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } +grumpkin = { version = "0.1.0", package = "noir_grumpkin", features = ["std"] } +ark-ec = { version = "^0.4.0", default-features = false } +ark-ff = { version = "^0.4.0", default-features = false } +ark-std = { version = "^0.4.0", default-features = false } + # Misc utils crates iter-extended = { path = "utils/iter-extended" } diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index 15d8aafffef..801935c90f9 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -17,9 +17,9 @@ hex.workspace = true num-bigint.workspace = true serde.workspace = true -ark-bn254 = { version = "^0.4.0", default-features = false, features = ["curve"] } -ark-bls12-381 = { version = "^0.4.0", optional = true, default-features = false, features = ["curve"] } -ark-ff = { version = "^0.4.0", default-features = false } +ark-bn254.workspace = true +ark-bls12-381 = { workspace = true, optional = true } +ark-ff.workspace = true cfg-if = "1.0.0" diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index 132dddd50e5..37d40de71a9 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -18,14 +18,14 @@ acvm_blackbox_solver.workspace = true hex.workspace = true lazy_static = "1.4" -# BN254 fixed base scalar multiplication solver -grumpkin = { version = "0.1.0", package = "noir_grumpkin", features = ["std"] } -ark-ec = { version = "^0.4.0", default-features = false } -ark-ff = { version = "^0.4.0", default-features = false } +ark-bn254.workspace = true +grumpkin.workspace = true +ark-ec.workspace = true +ark-ff.workspace = true num-bigint.workspace = true [dev-dependencies] -ark-std = { version = "^0.4.0", default-features = false } +ark-std.workspace = true criterion = "0.5.0" pprof = { version = "0.12", features = [ "flamegraph", @@ -36,7 +36,3 @@ pprof = { version = "0.12", features = [ [[bench]] name = "criterion" harness = false - -[features] -default = ["bn254"] -bn254 = ["acir/bn254"] diff --git a/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs b/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs index 148accd8b23..35cc68051d7 100644 --- a/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs +++ b/acvm-repo/bn254_blackbox_solver/src/embedded_curve_ops.rs @@ -3,8 +3,9 @@ use ark_ec::AffineRepr; use ark_ff::MontConfig; use num_bigint::BigUint; +use crate::FieldElement; +use acir::AcirField; use acir::BlackBoxFunc; -use acir::{AcirField, FieldElement}; use crate::BlackBoxResolutionError; diff --git a/acvm-repo/bn254_blackbox_solver/src/lib.rs b/acvm-repo/bn254_blackbox_solver/src/lib.rs index e3cea1153be..08e0fb66a6d 100644 --- a/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -2,7 +2,6 @@ #![warn(clippy::semicolon_if_nothing_returned)] #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] -use acir::FieldElement; use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; mod embedded_curve_ops; @@ -15,6 +14,10 @@ use ark_ec::AffineRepr; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use poseidon2::poseidon2_permutation; +// Temporary hack, this ensure that we always use a bn254 field here +// without polluting the feature flags of the `acir_field` crate. +type FieldElement = acir::acir_field::GenericFieldElement; + #[derive(Default)] pub struct Bn254BlackBoxSolver; diff --git a/acvm-repo/bn254_blackbox_solver/src/pedersen/commitment.rs b/acvm-repo/bn254_blackbox_solver/src/pedersen/commitment.rs index 5ff2c269d8c..03f03fcf5ab 100644 --- a/acvm-repo/bn254_blackbox_solver/src/pedersen/commitment.rs +++ b/acvm-repo/bn254_blackbox_solver/src/pedersen/commitment.rs @@ -27,12 +27,13 @@ pub(crate) fn commit_native_with_index( #[cfg(test)] mod test { - use acir::{AcirField, FieldElement}; + use acir::AcirField; use ark_ec::short_weierstrass::Affine; use ark_std::{One, Zero}; use grumpkin::Fq; use crate::pedersen::commitment::commit_native_with_index; + use crate::FieldElement; #[test] fn commitment() { diff --git a/acvm-repo/bn254_blackbox_solver/src/pedersen/hash.rs b/acvm-repo/bn254_blackbox_solver/src/pedersen/hash.rs index 5c637207491..152526a9943 100644 --- a/acvm-repo/bn254_blackbox_solver/src/pedersen/hash.rs +++ b/acvm-repo/bn254_blackbox_solver/src/pedersen/hash.rs @@ -30,8 +30,9 @@ fn length_generator() -> &'static Affine { pub(crate) mod test { use super::*; + use crate::FieldElement; - use acir::{AcirField, FieldElement}; + use acir::AcirField; use ark_std::One; use grumpkin::Fq; diff --git a/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs b/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs index 95c620aab0d..18ed0b1d8ab 100644 --- a/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs +++ b/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs @@ -1,7 +1,9 @@ -use acir::{AcirField, FieldElement}; +use acir::AcirField; use acvm_blackbox_solver::BlackBoxResolutionError; use lazy_static::lazy_static; +use crate::FieldElement; + pub fn poseidon2_permutation( inputs: &[FieldElement], len: u32, @@ -543,9 +545,9 @@ impl<'a> Poseidon2<'a> { #[cfg(test)] mod test { - use acir::{AcirField, FieldElement}; + use acir::AcirField; - use super::{field_from_hex, poseidon2_permutation}; + use super::{field_from_hex, poseidon2_permutation, FieldElement}; #[test] fn smoke_test() { diff --git a/acvm-repo/bn254_blackbox_solver/src/schnorr/mod.rs b/acvm-repo/bn254_blackbox_solver/src/schnorr/mod.rs index 62e515a0792..8e3a40803f8 100644 --- a/acvm-repo/bn254_blackbox_solver/src/schnorr/mod.rs +++ b/acvm-repo/bn254_blackbox_solver/src/schnorr/mod.rs @@ -65,9 +65,10 @@ fn schnorr_generate_challenge( #[cfg(test)] mod schnorr_tests { - use acir::{AcirField, FieldElement}; + use acir::AcirField; use super::verify_signature; + use crate::FieldElement; #[test] fn verifies_valid_signature() { From e3287668b772959c24ae05a6f006dbc10ccfd9bc Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:57:24 +0100 Subject: [PATCH 16/57] chore: push code related to ABI gen into `noirc_driver` (#5218) # Description ## Problem\* Resolves ## Summary\* I'm not sure why this code was placed here. This code is only relevant for ABI gen so I've pushed it into the`abi_gen` module. This simplifies the dependency graph. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- Cargo.lock | 1 - compiler/noirc_driver/src/abi_gen.rs | 106 +++++++++++++++++++++++++-- compiler/noirc_driver/src/lib.rs | 4 +- tooling/noirc_abi/Cargo.toml | 11 +-- tooling/noirc_abi/src/lib.rs | 102 -------------------------- 5 files changed, 102 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd575d0dca2..a8eab53dc07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2727,7 +2727,6 @@ version = "0.30.0" dependencies = [ "acvm", "iter-extended", - "noirc_frontend", "noirc_printable_type", "num-bigint", "num-traits", diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 71dd1b18761..e2c7fea3c07 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -2,14 +2,17 @@ use std::collections::BTreeMap; use acvm::acir::circuit::ErrorSelector; use iter_extended::vecmap; -use noirc_abi::{Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue}; -use noirc_frontend::ast::Visibility; +use noirc_abi::{ + Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, +}; +use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::{ hir::Context, hir_def::{expr::HirArrayLiteral, function::Param, stmt::HirPattern, types::Type}, macros_api::{HirExpression, HirLiteral}, node_interner::{FuncId, NodeInterner}, }; +use noirc_frontend::{TypeBinding, TypeVariableKind}; /// Arranges a function signature and a generated circuit's return witnesses into a /// `noirc_abi::Abi`. @@ -20,15 +23,102 @@ pub(super) fn gen_abi( error_types: BTreeMap, ) -> Abi { let (parameters, return_type) = compute_function_abi(context, func_id); - let return_type = return_type - .map(|typ| AbiReturnType { abi_type: typ, visibility: return_visibility.into() }); + let return_type = return_type.map(|typ| AbiReturnType { + abi_type: typ, + visibility: to_abi_visibility(return_visibility), + }); let error_types = error_types .into_iter() - .map(|(selector, typ)| (selector, AbiErrorType::from_type(context, &typ))) + .map(|(selector, typ)| (selector, build_abi_error_type(context, &typ))) .collect(); Abi { parameters, return_type, error_types } } +fn build_abi_error_type(context: &Context, typ: &Type) -> AbiErrorType { + match typ { + Type::FmtString(len, item_types) => { + let length = len.evaluate_to_u32().expect("Cannot evaluate fmt length"); + let Type::Tuple(item_types) = item_types.as_ref() else { + unreachable!("FmtString items must be a tuple") + }; + let item_types = + item_types.iter().map(|typ| abi_type_from_hir_type(context, typ)).collect(); + AbiErrorType::FmtString { length, item_types } + } + _ => AbiErrorType::Custom(abi_type_from_hir_type(context, typ)), + } +} + +pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { + match typ { + Type::FieldElement => AbiType::Field, + Type::Array(size, typ) => { + let length = size + .evaluate_to_u32() + .expect("Cannot have variable sized arrays as a parameter to main"); + let typ = typ.as_ref(); + AbiType::Array { length, typ: Box::new(abi_type_from_hir_type(context, typ)) } + } + Type::Integer(sign, bit_width) => { + let sign = match sign { + Signedness::Unsigned => Sign::Unsigned, + Signedness::Signed => Sign::Signed, + }; + + AbiType::Integer { sign, width: (*bit_width).into() } + } + Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) + | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { + TypeBinding::Bound(typ) => abi_type_from_hir_type(context, typ), + TypeBinding::Unbound(_) => { + abi_type_from_hir_type(context, &Type::default_int_or_field_type()) + } + }, + Type::Bool => AbiType::Boolean, + Type::String(size) => { + let size = size + .evaluate_to_u32() + .expect("Cannot have variable sized strings as a parameter to main"); + AbiType::String { length: size } + } + + Type::Struct(def, args) => { + let struct_type = def.borrow(); + let fields = struct_type.get_fields(args); + let fields = + vecmap(fields, |(name, typ)| (name, abi_type_from_hir_type(context, &typ))); + // For the ABI, we always want to resolve the struct paths from the root crate + let path = context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id); + AbiType::Struct { fields, path } + } + Type::Alias(def, args) => abi_type_from_hir_type(context, &def.borrow().get_type(args)), + Type::Tuple(fields) => { + let fields = vecmap(fields, |typ| abi_type_from_hir_type(context, typ)); + AbiType::Tuple { fields } + } + Type::Error + | Type::Unit + | Type::Constant(_) + | Type::TraitAsType(..) + | Type::TypeVariable(_, _) + | Type::NamedGeneric(..) + | Type::Forall(..) + | Type::Code + | Type::Slice(_) + | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), + Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), + Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"), + } +} + +fn to_abi_visibility(value: Visibility) -> AbiVisibility { + match value { + Visibility::Public => AbiVisibility::Public, + Visibility::Private => AbiVisibility::Private, + Visibility::DataBus => AbiVisibility::DataBus, + } +} + pub(super) fn compute_function_abi( context: &Context, func_id: &FuncId, @@ -37,7 +127,7 @@ pub(super) fn compute_function_abi( let (parameters, return_type) = func_meta.function_signature(); let parameters = into_abi_params(context, parameters); - let return_type = return_type.map(|typ| AbiType::from_type(context, &typ)); + let return_type = return_type.map(|typ| abi_type_from_hir_type(context, &typ)); (parameters, return_type) } @@ -57,8 +147,8 @@ fn into_abi_params(context: &Context, params: Vec) -> Vec { let param_name = get_param_name(&pattern, &context.def_interner) .expect("Abi for tuple and struct parameters is unimplemented") .to_owned(); - let as_abi = AbiType::from_type(context, &typ); - AbiParameter { name: param_name, typ: as_abi, visibility: vis.into() } + let as_abi = abi_type_from_hir_type(context, &typ); + AbiParameter { name: param_name, typ: as_abi, visibility: to_abi_visibility(vis) } }) } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index f8043a60f8c..2c8679eaa95 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -3,7 +3,7 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] -use abi_gen::value_from_hir_expression; +use abi_gen::{abi_type_from_hir_type, value_from_hir_expression}; use acvm::acir::circuit::ExpressionWidth; use clap::Args; use fm::{FileId, FileManager}; @@ -468,7 +468,7 @@ fn compile_contract_inner( let typ = context.def_interner.get_struct(struct_id); let typ = typ.borrow(); let fields = vecmap(typ.get_fields(&[]), |(name, typ)| { - (name, AbiType::from_type(context, &typ)) + (name, abi_type_from_hir_type(context, &typ)) }); let path = context.fully_qualified_struct_path(context.root_crate_id(), typ.id); diff --git a/tooling/noirc_abi/Cargo.toml b/tooling/noirc_abi/Cargo.toml index baae2dfa35e..d450b268aee 100644 --- a/tooling/noirc_abi/Cargo.toml +++ b/tooling/noirc_abi/Cargo.toml @@ -11,7 +11,6 @@ license.workspace = true [dependencies] acvm.workspace = true iter-extended.workspace = true -noirc_frontend.workspace = true noirc_printable_type.workspace = true toml.workspace = true serde_json = "1.0" @@ -25,11 +24,5 @@ strum = "0.24" strum_macros = "0.24" [features] -bn254 = [ - "acvm/bn254", - "noirc_frontend/bn254", -] -bls12_381 = [ - "acvm/bls12_381", - "noirc_frontend/bls12_381", -] \ No newline at end of file +bn254 = ["acvm/bn254"] +bls12_381 = ["acvm/bls12_381"] diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 514fac2e73d..2ebccbad1a0 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -13,8 +13,6 @@ use acvm::{ use errors::AbiError; use input_parser::InputValue; use iter_extended::{try_btree_map, try_vecmap, vecmap}; -use noirc_frontend::ast::{Signedness, Visibility}; -use noirc_frontend::{hir::Context, Type, TypeBinding, TypeVariableKind}; use noirc_printable_type::{ decode_value as printable_type_decode_value, PrintableType, PrintableValue, PrintableValueDisplay, @@ -87,26 +85,6 @@ pub enum AbiVisibility { DataBus, } -impl From for AbiVisibility { - fn from(value: Visibility) -> Self { - match value { - Visibility::Public => AbiVisibility::Public, - Visibility::Private => AbiVisibility::Private, - Visibility::DataBus => AbiVisibility::DataBus, - } - } -} - -impl From<&Visibility> for AbiVisibility { - fn from(value: &Visibility) -> Self { - match value { - Visibility::Public => AbiVisibility::Public, - Visibility::Private => AbiVisibility::Private, - Visibility::DataBus => AbiVisibility::DataBus, - } - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Sign { @@ -115,70 +93,6 @@ pub enum Sign { } impl AbiType { - pub fn from_type(context: &Context, typ: &Type) -> Self { - // Note; use strict_eq instead of partial_eq when comparing field types - // in this method, you most likely want to distinguish between public and private - match typ { - Type::FieldElement => Self::Field, - Type::Array(size, typ) => { - let length = size - .evaluate_to_u32() - .expect("Cannot have variable sized arrays as a parameter to main"); - let typ = typ.as_ref(); - Self::Array { length, typ: Box::new(Self::from_type(context, typ)) } - } - Type::Integer(sign, bit_width) => { - let sign = match sign { - Signedness::Unsigned => Sign::Unsigned, - Signedness::Signed => Sign::Signed, - }; - - Self::Integer { sign, width: (*bit_width).into() } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { - TypeBinding::Bound(typ) => Self::from_type(context, typ), - TypeBinding::Unbound(_) => { - Self::from_type(context, &Type::default_int_or_field_type()) - } - }, - Type::Bool => Self::Boolean, - Type::String(size) => { - let size = size - .evaluate_to_u32() - .expect("Cannot have variable sized strings as a parameter to main"); - Self::String { length: size } - } - - Type::Struct(def, args) => { - let struct_type = def.borrow(); - let fields = struct_type.get_fields(args); - let fields = vecmap(fields, |(name, typ)| (name, Self::from_type(context, &typ))); - // For the ABI, we always want to resolve the struct paths from the root crate - let path = - context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id); - Self::Struct { fields, path } - } - Type::Alias(def, args) => Self::from_type(context, &def.borrow().get_type(args)), - Type::Tuple(fields) => { - let fields = vecmap(fields, |typ| Self::from_type(context, typ)); - Self::Tuple { fields } - } - Type::Error - | Type::Unit - | Type::Constant(_) - | Type::TraitAsType(..) - | Type::TypeVariable(_, _) - | Type::NamedGeneric(..) - | Type::Forall(..) - | Type::Code - | Type::Slice(_) - | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), - Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), - Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"), - } - } - /// Returns the number of field elements required to represent the type once encoded. pub fn field_count(&self) -> u32 { match self { @@ -543,22 +457,6 @@ pub enum AbiErrorType { FmtString { length: u32, item_types: Vec }, Custom(AbiType), } -impl AbiErrorType { - pub fn from_type(context: &Context, typ: &Type) -> Self { - match typ { - Type::FmtString(len, item_types) => { - let length = len.evaluate_to_u32().expect("Cannot evaluate fmt length"); - let Type::Tuple(item_types) = item_types.as_ref() else { - unreachable!("FmtString items must be a tuple") - }; - let item_types = - item_types.iter().map(|typ| AbiType::from_type(context, typ)).collect(); - Self::FmtString { length, item_types } - } - _ => Self::Custom(AbiType::from_type(context, typ)), - } - } -} pub fn display_abi_error( fields: &[F], From 3e04cd5e122d83396f75818e78d45c65de178640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Wed, 12 Jun 2024 12:25:38 +0100 Subject: [PATCH 17/57] chore: fixing all relative paths (#5220) Making the change as per the [docusaurus documentation](https://docusaurus.io/docs/advanced/routing#docs-routing) as this avoids mistakes when for some reason a link comes with a trailing slash, and other benefits --- docs/docs/noir/concepts/data_types/arrays.md | 4 +++- .../docs/noir/concepts/data_types/booleans.md | 4 ++-- docs/docs/noir/concepts/data_types/strings.md | 2 +- docs/docs/noir/concepts/generics.md | 4 ++-- docs/docs/noir/concepts/unconstrained.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- docs/docs/noir/standard_library/recursion.md | 2 +- docs/docs/tooling/debugger.md | 2 +- .../getting_started/01_hello_world.md | 4 ++-- .../getting_started/02_breakdown.md | 4 ++-- docs/versioned_docs/version-v0.17.0/index.md | 4 ++-- .../data_types/02_booleans.md | 4 ++-- .../data_types/03_strings.md | 2 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../version-v0.17.0/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 20 ++++++++--------- .../standard_library/recursion.md | 2 +- .../getting_started/01_hello_world.md | 4 ++-- .../getting_started/02_breakdown.md | 4 ++-- docs/versioned_docs/version-v0.19.0/index.md | 4 ++-- .../data_types/02_booleans.md | 4 ++-- .../data_types/03_strings.md | 2 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../version-v0.19.0/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 22 +++++++++---------- .../standard_library/recursion.md | 2 +- .../getting_started/01_hello_world.md | 4 ++-- .../getting_started/02_breakdown.md | 4 ++-- docs/versioned_docs/version-v0.19.1/index.md | 4 ++-- .../data_types/02_booleans.md | 4 ++-- .../data_types/03_strings.md | 2 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../version-v0.19.1/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 22 +++++++++---------- .../standard_library/recursion.md | 2 +- .../getting_started/01_hello_world.md | 4 ++-- .../getting_started/02_breakdown.md | 4 ++-- docs/versioned_docs/version-v0.19.2/index.md | 4 ++-- .../data_types/02_booleans.md | 4 ++-- .../data_types/03_strings.md | 2 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../version-v0.19.2/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 22 +++++++++---------- .../standard_library/recursion.md | 2 +- .../getting_started/01_hello_world.md | 4 ++-- .../getting_started/02_breakdown.md | 4 ++-- docs/versioned_docs/version-v0.19.3/index.md | 4 ++-- .../data_types/02_booleans.md | 4 ++-- .../data_types/03_strings.md | 2 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../version-v0.19.3/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 22 +++++++++---------- .../standard_library/recursion.md | 2 +- .../getting_started/01_hello_world.md | 4 ++-- .../getting_started/02_breakdown.md | 4 ++-- docs/versioned_docs/version-v0.19.4/index.md | 4 ++-- .../data_types/02_booleans.md | 4 ++-- .../data_types/03_strings.md | 2 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../version-v0.19.4/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 22 +++++++++---------- .../standard_library/recursion.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 22 +++++++++---------- .../noir/standard_library/recursion.md | 2 +- .../noir/syntax/data_types/arrays.md | 2 +- .../noir/syntax/data_types/booleans.md | 4 ++-- .../noir/syntax/data_types/strings.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.23.0/noir/concepts/generics.md | 4 ++-- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 20 ++++++++--------- .../noir/standard_library/recursion.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.24.0/noir/concepts/generics.md | 4 ++-- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.25.0/noir/concepts/generics.md | 4 ++-- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.26.0/noir/concepts/generics.md | 4 ++-- .../noir/concepts/unconstrained.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.27.0/noir/concepts/generics.md | 4 ++-- .../noir/concepts/unconstrained.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.28.0/noir/concepts/generics.md | 4 ++-- .../noir/concepts/unconstrained.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../version-v0.28.0/tooling/debugger.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.29.0/noir/concepts/generics.md | 4 ++-- .../noir/concepts/unconstrained.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../version-v0.29.0/tooling/debugger.md | 2 +- .../noir/concepts/data_types/arrays.md | 4 +++- .../noir/concepts/data_types/booleans.md | 4 ++-- .../noir/concepts/data_types/strings.md | 2 +- .../version-v0.30.0/noir/concepts/generics.md | 4 ++-- .../noir/concepts/unconstrained.md | 2 +- .../modules_packages_crates/dependencies.md | 4 ++-- .../noir/standard_library/black_box_fns.md | 2 +- .../noir/standard_library/recursion.md | 2 +- .../version-v0.30.0/tooling/debugger.md | 2 +- 139 files changed, 294 insertions(+), 276 deletions(-) diff --git a/docs/docs/noir/concepts/data_types/arrays.md b/docs/docs/noir/concepts/data_types/arrays.md index efce3e95d32..95d749053e2 100644 --- a/docs/docs/noir/concepts/data_types/arrays.md +++ b/docs/docs/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = &[]; ``` diff --git a/docs/docs/noir/concepts/data_types/booleans.md b/docs/docs/noir/concepts/data_types/booleans.md index 3dcfa836814..2507af710e7 100644 --- a/docs/docs/noir/concepts/data_types/booleans.md +++ b/docs/docs/noir/concepts/data_types/booleans.md @@ -24,5 +24,5 @@ fn main() { ``` The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/docs/noir/concepts/data_types/strings.md b/docs/docs/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/docs/noir/concepts/data_types/strings.md +++ b/docs/docs/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/docs/noir/concepts/generics.md b/docs/docs/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/docs/noir/concepts/generics.md +++ b/docs/docs/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/docs/noir/concepts/unconstrained.md b/docs/docs/noir/concepts/unconstrained.md index b8e71fe65f0..96f824c5e42 100644 --- a/docs/docs/noir/concepts/unconstrained.md +++ b/docs/docs/noir/concepts/unconstrained.md @@ -96,4 +96,4 @@ Generally we want to use brillig whenever there's something that's easy to verif ## Break and Continue -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/docs/noir/modules_packages_crates/dependencies.md b/docs/docs/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/docs/noir/modules_packages_crates/dependencies.md +++ b/docs/docs/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/docs/noir/standard_library/black_box_fns.md b/docs/docs/noir/standard_library/black_box_fns.md index eeead580969..d5694250f05 100644 --- a/docs/docs/noir/standard_library/black_box_fns.md +++ b/docs/docs/noir/standard_library/black_box_fns.md @@ -25,7 +25,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/docs/noir/standard_library/recursion.md b/docs/docs/noir/standard_library/recursion.md index a93894043dc..f33c285cf4e 100644 --- a/docs/docs/noir/standard_library/recursion.md +++ b/docs/docs/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/docs/tooling/debugger.md b/docs/docs/tooling/debugger.md index 7c158d949d1..9b7565ba9ff 100644 --- a/docs/docs/tooling/debugger.md +++ b/docs/docs/tooling/debugger.md @@ -12,7 +12,7 @@ There are currently two ways of debugging Noir programs: 1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). 2. Via the REPL debugger, which ships with Nargo. -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - Noir & Nargo ≥0.28.0 - Noir's VS Code extension ≥0.0.11 diff --git a/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md index d4daae605a2..34f8cd96fcd 100644 --- a/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md +++ b/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md @@ -74,7 +74,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../language_concepts/data_types) section. +[Data Types](../language_concepts/data_types.md) section. The next line of the program specifies its body: @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. ## Build In/Output Files diff --git a/docs/versioned_docs/version-v0.17.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.17.0/getting_started/02_breakdown.md index e7b1f33b339..8616feee917 100644 --- a/docs/versioned_docs/version-v0.17.0/getting_started/02_breakdown.md +++ b/docs/versioned_docs/version-v0.17.0/getting_started/02_breakdown.md @@ -51,7 +51,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: +Nargo.toml for a [workspace](../modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -74,7 +74,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies.md)for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. diff --git a/docs/versioned_docs/version-v0.17.0/index.md b/docs/versioned_docs/version-v0.17.0/index.md index 2d5e6f4454f..c88aabf0ac1 100644 --- a/docs/versioned_docs/version-v0.17.0/index.md +++ b/docs/versioned_docs/version-v0.17.0/index.md @@ -34,7 +34,7 @@ Noir can be used for a variety of purposes. ### Solidity Developers Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will -be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands.md#nargo-codegen-verifier) command to create a verifier contract. ### Protocol Developers @@ -96,4 +96,4 @@ Some libraries that are available today include: - [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir - [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers -See the section on [dependencies](./modules_packages_crates/dependencies) for more information. +See the section on [dependencies](./modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/02_booleans.md index 885db167d83..d353606210a 100644 --- a/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/02_booleans.md +++ b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/03_strings.md index c42f34ec3ad..4360893e9a2 100644 --- a/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/03_strings.md +++ b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/03_strings.md @@ -16,7 +16,7 @@ keywords: The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`std::println()`. See more about [Logging](../../standard_library/logging). +`std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/04_arrays.md index bdbd1798bef..1424ca2df14 100644 --- a/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/04_arrays.md +++ b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md index e91e73a4c4f..87a09293ea8 100644 --- a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md @@ -81,14 +81,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.17.0/nargo/01_commands.md b/docs/versioned_docs/version-v0.17.0/nargo/01_commands.md index 65e2bdb44e3..e2b0af522f4 100644 --- a/docs/versioned_docs/version-v0.17.0/nargo/01_commands.md +++ b/docs/versioned_docs/version-v0.17.0/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing). +See an example on the [testing page](./testing.md). ### Options diff --git a/docs/versioned_docs/version-v0.17.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.17.0/standard_library/black_box_fns.md index a412de19d06..b4dedefe4c9 100644 --- a/docs/versioned_docs/version-v0.17.0/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.17.0/standard_library/black_box_fns.md @@ -26,19 +26,19 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen](./cryptographic_primitives/hashes#pedersen) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen](./cryptographic_primitives/hashes.mdx#pedersen) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.17.0/standard_library/recursion.md b/docs/versioned_docs/version-v0.17.0/standard_library/recursion.md index ff4c63acaa7..5e592a2fd89 100644 --- a/docs/versioned_docs/version-v0.17.0/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.17.0/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md index d4daae605a2..34f8cd96fcd 100644 --- a/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md +++ b/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md @@ -74,7 +74,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../language_concepts/data_types) section. +[Data Types](../language_concepts/data_types.md) section. The next line of the program specifies its body: @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. ## Build In/Output Files diff --git a/docs/versioned_docs/version-v0.19.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.0/getting_started/02_breakdown.md index d28a54a1600..f928370b2e8 100644 --- a/docs/versioned_docs/version-v0.19.0/getting_started/02_breakdown.md +++ b/docs/versioned_docs/version-v0.19.0/getting_started/02_breakdown.md @@ -51,7 +51,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: +Nargo.toml for a [workspace](../modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -74,7 +74,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies.md)for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. diff --git a/docs/versioned_docs/version-v0.19.0/index.md b/docs/versioned_docs/version-v0.19.0/index.md index 380368db036..e8d86020a20 100644 --- a/docs/versioned_docs/version-v0.19.0/index.md +++ b/docs/versioned_docs/version-v0.19.0/index.md @@ -34,7 +34,7 @@ Noir can be used for a variety of purposes. ### Solidity Developers Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will -be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier.md) command to create a verifier contract. ### Protocol Developers @@ -97,4 +97,4 @@ Some libraries that are available today include: - [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir - [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers -See the section on [dependencies](./modules_packages_crates/dependencies) for more information. +See the section on [dependencies](./modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/02_booleans.md index 885db167d83..d353606210a 100644 --- a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/02_booleans.md +++ b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/03_strings.md index c42f34ec3ad..4360893e9a2 100644 --- a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/03_strings.md +++ b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/03_strings.md @@ -16,7 +16,7 @@ keywords: The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`std::println()`. See more about [Logging](../../standard_library/logging). +`std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/04_arrays.md index bdbd1798bef..1424ca2df14 100644 --- a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/04_arrays.md +++ b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md index e91e73a4c4f..87a09293ea8 100644 --- a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md @@ -81,14 +81,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.19.0/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.0/nargo/01_commands.md index 65e2bdb44e3..e2b0af522f4 100644 --- a/docs/versioned_docs/version-v0.19.0/nargo/01_commands.md +++ b/docs/versioned_docs/version-v0.19.0/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing). +See an example on the [testing page](./testing.md). ### Options diff --git a/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md index e0c6d475c1f..985bb7c879d 100644 --- a/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md @@ -26,20 +26,20 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.19.0/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.0/standard_library/recursion.md index ff4c63acaa7..5e592a2fd89 100644 --- a/docs/versioned_docs/version-v0.19.0/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.19.0/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md index d4daae605a2..34f8cd96fcd 100644 --- a/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md +++ b/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md @@ -74,7 +74,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../language_concepts/data_types) section. +[Data Types](../language_concepts/data_types.md) section. The next line of the program specifies its body: @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. ## Build In/Output Files diff --git a/docs/versioned_docs/version-v0.19.1/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.1/getting_started/02_breakdown.md index d28a54a1600..f928370b2e8 100644 --- a/docs/versioned_docs/version-v0.19.1/getting_started/02_breakdown.md +++ b/docs/versioned_docs/version-v0.19.1/getting_started/02_breakdown.md @@ -51,7 +51,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: +Nargo.toml for a [workspace](../modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -74,7 +74,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies.md)for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. diff --git a/docs/versioned_docs/version-v0.19.1/index.md b/docs/versioned_docs/version-v0.19.1/index.md index 380368db036..e8d86020a20 100644 --- a/docs/versioned_docs/version-v0.19.1/index.md +++ b/docs/versioned_docs/version-v0.19.1/index.md @@ -34,7 +34,7 @@ Noir can be used for a variety of purposes. ### Solidity Developers Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will -be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier.md) command to create a verifier contract. ### Protocol Developers @@ -97,4 +97,4 @@ Some libraries that are available today include: - [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir - [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers -See the section on [dependencies](./modules_packages_crates/dependencies) for more information. +See the section on [dependencies](./modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/02_booleans.md index 885db167d83..d353606210a 100644 --- a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/02_booleans.md +++ b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/03_strings.md index c42f34ec3ad..4360893e9a2 100644 --- a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/03_strings.md +++ b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/03_strings.md @@ -16,7 +16,7 @@ keywords: The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`std::println()`. See more about [Logging](../../standard_library/logging). +`std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/04_arrays.md index bdbd1798bef..1424ca2df14 100644 --- a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/04_arrays.md +++ b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md index e91e73a4c4f..87a09293ea8 100644 --- a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md @@ -81,14 +81,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.19.1/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.1/nargo/01_commands.md index 65e2bdb44e3..e2b0af522f4 100644 --- a/docs/versioned_docs/version-v0.19.1/nargo/01_commands.md +++ b/docs/versioned_docs/version-v0.19.1/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing). +See an example on the [testing page](./testing.md). ### Options diff --git a/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md index e0c6d475c1f..985bb7c879d 100644 --- a/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md @@ -26,20 +26,20 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.19.1/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.1/standard_library/recursion.md index ff4c63acaa7..5e592a2fd89 100644 --- a/docs/versioned_docs/version-v0.19.1/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.19.1/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md index d4daae605a2..34f8cd96fcd 100644 --- a/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md +++ b/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md @@ -74,7 +74,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../language_concepts/data_types) section. +[Data Types](../language_concepts/data_types.md) section. The next line of the program specifies its body: @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. ## Build In/Output Files diff --git a/docs/versioned_docs/version-v0.19.2/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.2/getting_started/02_breakdown.md index d28a54a1600..f928370b2e8 100644 --- a/docs/versioned_docs/version-v0.19.2/getting_started/02_breakdown.md +++ b/docs/versioned_docs/version-v0.19.2/getting_started/02_breakdown.md @@ -51,7 +51,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: +Nargo.toml for a [workspace](../modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -74,7 +74,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies.md)for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. diff --git a/docs/versioned_docs/version-v0.19.2/index.md b/docs/versioned_docs/version-v0.19.2/index.md index 380368db036..e8d86020a20 100644 --- a/docs/versioned_docs/version-v0.19.2/index.md +++ b/docs/versioned_docs/version-v0.19.2/index.md @@ -34,7 +34,7 @@ Noir can be used for a variety of purposes. ### Solidity Developers Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will -be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier.md) command to create a verifier contract. ### Protocol Developers @@ -97,4 +97,4 @@ Some libraries that are available today include: - [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir - [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers -See the section on [dependencies](./modules_packages_crates/dependencies) for more information. +See the section on [dependencies](./modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/02_booleans.md index 885db167d83..d353606210a 100644 --- a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/02_booleans.md +++ b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/03_strings.md index c42f34ec3ad..4360893e9a2 100644 --- a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/03_strings.md +++ b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/03_strings.md @@ -16,7 +16,7 @@ keywords: The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`std::println()`. See more about [Logging](../../standard_library/logging). +`std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/04_arrays.md index bdbd1798bef..1424ca2df14 100644 --- a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/04_arrays.md +++ b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md index e91e73a4c4f..87a09293ea8 100644 --- a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md @@ -81,14 +81,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.19.2/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.2/nargo/01_commands.md index 65e2bdb44e3..e2b0af522f4 100644 --- a/docs/versioned_docs/version-v0.19.2/nargo/01_commands.md +++ b/docs/versioned_docs/version-v0.19.2/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing). +See an example on the [testing page](./testing.md). ### Options diff --git a/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md index e0c6d475c1f..985bb7c879d 100644 --- a/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md @@ -26,20 +26,20 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.19.2/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.2/standard_library/recursion.md index ff4c63acaa7..5e592a2fd89 100644 --- a/docs/versioned_docs/version-v0.19.2/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.19.2/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md index d4daae605a2..34f8cd96fcd 100644 --- a/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md +++ b/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md @@ -74,7 +74,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../language_concepts/data_types) section. +[Data Types](../language_concepts/data_types.md) section. The next line of the program specifies its body: @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. ## Build In/Output Files diff --git a/docs/versioned_docs/version-v0.19.3/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.3/getting_started/02_breakdown.md index 7a7fb876c35..10422029661 100644 --- a/docs/versioned_docs/version-v0.19.3/getting_started/02_breakdown.md +++ b/docs/versioned_docs/version-v0.19.3/getting_started/02_breakdown.md @@ -51,7 +51,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: +Nargo.toml for a [workspace](../modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -74,7 +74,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies.md)for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. diff --git a/docs/versioned_docs/version-v0.19.3/index.md b/docs/versioned_docs/version-v0.19.3/index.md index 380368db036..e8d86020a20 100644 --- a/docs/versioned_docs/version-v0.19.3/index.md +++ b/docs/versioned_docs/version-v0.19.3/index.md @@ -34,7 +34,7 @@ Noir can be used for a variety of purposes. ### Solidity Developers Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will -be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier.md) command to create a verifier contract. ### Protocol Developers @@ -97,4 +97,4 @@ Some libraries that are available today include: - [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir - [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers -See the section on [dependencies](./modules_packages_crates/dependencies) for more information. +See the section on [dependencies](./modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/02_booleans.md index 885db167d83..d353606210a 100644 --- a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/02_booleans.md +++ b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/03_strings.md index c42f34ec3ad..4360893e9a2 100644 --- a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/03_strings.md +++ b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/03_strings.md @@ -16,7 +16,7 @@ keywords: The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`std::println()`. See more about [Logging](../../standard_library/logging). +`std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/04_arrays.md index bdbd1798bef..1424ca2df14 100644 --- a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/04_arrays.md +++ b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md index e91e73a4c4f..87a09293ea8 100644 --- a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md @@ -81,14 +81,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.19.3/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.3/nargo/01_commands.md index 65e2bdb44e3..e2b0af522f4 100644 --- a/docs/versioned_docs/version-v0.19.3/nargo/01_commands.md +++ b/docs/versioned_docs/version-v0.19.3/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing). +See an example on the [testing page](./testing.md). ### Options diff --git a/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md index e0c6d475c1f..985bb7c879d 100644 --- a/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md @@ -26,20 +26,20 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.19.3/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.3/standard_library/recursion.md index ff4c63acaa7..5e592a2fd89 100644 --- a/docs/versioned_docs/version-v0.19.3/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.19.3/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md index d4daae605a2..34f8cd96fcd 100644 --- a/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md +++ b/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md @@ -74,7 +74,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../language_concepts/data_types) section. +[Data Types](../language_concepts/data_types.md) section. The next line of the program specifies its body: @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. ## Build In/Output Files diff --git a/docs/versioned_docs/version-v0.19.4/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.4/getting_started/02_breakdown.md index d28a54a1600..f928370b2e8 100644 --- a/docs/versioned_docs/version-v0.19.4/getting_started/02_breakdown.md +++ b/docs/versioned_docs/version-v0.19.4/getting_started/02_breakdown.md @@ -51,7 +51,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: +Nargo.toml for a [workspace](../modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -74,7 +74,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies.md)for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. diff --git a/docs/versioned_docs/version-v0.19.4/index.md b/docs/versioned_docs/version-v0.19.4/index.md index 380368db036..e8d86020a20 100644 --- a/docs/versioned_docs/version-v0.19.4/index.md +++ b/docs/versioned_docs/version-v0.19.4/index.md @@ -34,7 +34,7 @@ Noir can be used for a variety of purposes. ### Solidity Developers Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will -be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +be modularized in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier.md) command to create a verifier contract. ### Protocol Developers @@ -97,4 +97,4 @@ Some libraries that are available today include: - [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir - [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers -See the section on [dependencies](./modules_packages_crates/dependencies) for more information. +See the section on [dependencies](./modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md index 885db167d83..d353606210a 100644 --- a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md +++ b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/03_strings.md index b4c75942bb8..baa3f205094 100644 --- a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/03_strings.md +++ b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/03_strings.md @@ -15,7 +15,7 @@ keywords: The string type is a fixed length value defined with `str`. -You can use strings in `assert()` functions or print them with `std::println()`. See more about [Logging](../../standard_library/logging). +You can use strings in `assert()` functions or print them with `std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md index bdbd1798bef..1424ca2df14 100644 --- a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md +++ b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md index e91e73a4c4f..87a09293ea8 100644 --- a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md @@ -81,14 +81,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md index 65e2bdb44e3..e2b0af522f4 100644 --- a/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md +++ b/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing). +See an example on the [testing page](./testing.md). ### Options diff --git a/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md index e0c6d475c1f..985bb7c879d 100644 --- a/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md @@ -26,20 +26,20 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.19.4/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.4/standard_library/recursion.md index ff4c63acaa7..5e592a2fd89 100644 --- a/docs/versioned_docs/version-v0.19.4/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.19.4/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md index a37dc401b7d..d9d21ef0485 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.22.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.22.0/noir/standard_library/black_box_fns.md index e0c6d475c1f..985bb7c879d 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.22.0/noir/standard_library/black_box_fns.md @@ -26,20 +26,20 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/docs/versioned_docs/version-v0.22.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.22.0/noir/standard_library/recursion.md index 67962082a8f..4a004fd3664 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.22.0/noir/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/arrays.md b/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/arrays.md index 075d39dadd4..4c80d50ed01 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/booleans.md b/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/strings.md b/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/strings.md index 8d76d4ca654..dd6c844f6ae 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/strings.md +++ b/docs/versioned_docs/version-v0.22.0/noir/syntax/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`std::println()`. See more about [Logging](../../standard_library/logging). +`std::println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md index 7f275a2d771..d95346454a9 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = []; ``` diff --git a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md index a37dc401b7d..d9d21ef0485 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md index eae8744abf0..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md @@ -12,19 +12,19 @@ The ACVM spec defines a set of blackbox functions which backends will be expecte Here is a list of the current black box functions: -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Blake3](./cryptographic_primitives/hashes#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md index f252150c8b5..4390bda4a26 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md @@ -15,7 +15,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/arrays.md index a8bd338e736..ca54d82b26b 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = []; ``` diff --git a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.24.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.24.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.24.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/dependencies.md index a37dc401b7d..d9d21ef0485 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md index be8c65679c3..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md @@ -24,7 +24,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.24.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.24.0/noir/standard_library/recursion.md index 9337499dac8..ed2ed01fceb 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.24.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md index a8bd338e736..ca54d82b26b 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = []; ``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md index be8c65679c3..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md @@ -24,7 +24,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md index 9337499dac8..ed2ed01fceb 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md index efce3e95d32..95d749053e2 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = &[]; ``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md index b8e71fe65f0..96f824c5e42 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md @@ -96,4 +96,4 @@ Generally we want to use brillig whenever there's something that's easy to verif ## Break and Continue -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md index be8c65679c3..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md @@ -24,7 +24,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md index a93894043dc..f33c285cf4e 100644 --- a/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md index efce3e95d32..95d749053e2 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = &[]; ``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md index b8e71fe65f0..96f824c5e42 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md @@ -96,4 +96,4 @@ Generally we want to use brillig whenever there's something that's easy to verif ## Break and Continue -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md index be8c65679c3..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md @@ -24,7 +24,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md index a93894043dc..f33c285cf4e 100644 --- a/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/arrays.md index efce3e95d32..95d749053e2 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = &[]; ``` diff --git a/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.28.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.28.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.28.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.28.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.28.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.28.0/noir/concepts/unconstrained.md index b8e71fe65f0..96f824c5e42 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/concepts/unconstrained.md +++ b/docs/versioned_docs/version-v0.28.0/noir/concepts/unconstrained.md @@ -96,4 +96,4 @@ Generally we want to use brillig whenever there's something that's easy to verif ## Break and Continue -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.28.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.28.0/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.28.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.28.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.28.0/noir/standard_library/black_box_fns.md index be8c65679c3..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.28.0/noir/standard_library/black_box_fns.md @@ -24,7 +24,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.28.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.28.0/noir/standard_library/recursion.md index a93894043dc..f33c285cf4e 100644 --- a/docs/versioned_docs/version-v0.28.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.28.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.28.0/tooling/debugger.md b/docs/versioned_docs/version-v0.28.0/tooling/debugger.md index 7c158d949d1..9b7565ba9ff 100644 --- a/docs/versioned_docs/version-v0.28.0/tooling/debugger.md +++ b/docs/versioned_docs/version-v0.28.0/tooling/debugger.md @@ -12,7 +12,7 @@ There are currently two ways of debugging Noir programs: 1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). 2. Via the REPL debugger, which ships with Nargo. -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - Noir & Nargo ≥0.28.0 - Noir's VS Code extension ≥0.0.11 diff --git a/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/arrays.md index efce3e95d32..95d749053e2 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = &[]; ``` diff --git a/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.29.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.29.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.29.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.29.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.29.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.29.0/noir/concepts/unconstrained.md index b8e71fe65f0..96f824c5e42 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/concepts/unconstrained.md +++ b/docs/versioned_docs/version-v0.29.0/noir/concepts/unconstrained.md @@ -96,4 +96,4 @@ Generally we want to use brillig whenever there's something that's easy to verif ## Break and Continue -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.29.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.29.0/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.29.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.29.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.29.0/noir/standard_library/black_box_fns.md index be8c65679c3..e8b62f21d4e 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.29.0/noir/standard_library/black_box_fns.md @@ -24,7 +24,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.29.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.29.0/noir/standard_library/recursion.md index a93894043dc..f33c285cf4e 100644 --- a/docs/versioned_docs/version-v0.29.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.29.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.29.0/tooling/debugger.md b/docs/versioned_docs/version-v0.29.0/tooling/debugger.md index 7c158d949d1..9b7565ba9ff 100644 --- a/docs/versioned_docs/version-v0.29.0/tooling/debugger.md +++ b/docs/versioned_docs/version-v0.29.0/tooling/debugger.md @@ -12,7 +12,7 @@ There are currently two ways of debugging Noir programs: 1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). 2. Via the REPL debugger, which ships with Nargo. -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - Noir & Nargo ≥0.28.0 - Noir's VS Code extension ≥0.0.11 diff --git a/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/arrays.md index efce3e95d32..95d749053e2 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/arrays.md +++ b/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/arrays.md @@ -57,7 +57,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; @@ -70,7 +70,9 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` + However, multidimensional slices are not supported. For example, the following code will error at compile time: + ```rust let slice : [[Field]] = &[]; ``` diff --git a/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/booleans.md index 69826fcd724..7211716f63e 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/booleans.md +++ b/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/booleans.md @@ -27,5 +27,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/strings.md index 311dfd64416..8ab5825a4c4 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/strings.md +++ b/docs/versioned_docs/version-v0.30.0/noir/concepts/data_types/strings.md @@ -17,7 +17,7 @@ sidebar_position: 3 The string type is a fixed length value defined with `str`. You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). +`println()`. See more about [Logging](../../standard_library/logging.md). ```rust use dep::std; diff --git a/docs/versioned_docs/version-v0.30.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.30.0/noir/concepts/generics.md index ddd42bf1f9b..0c1c27a2221 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/concepts/generics.md +++ b/docs/versioned_docs/version-v0.30.0/noir/concepts/generics.md @@ -73,7 +73,7 @@ impl BigInt { Since a generic type `T` can represent any type, how can we call functions on the underlying type? In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over any type `T` that implements the `Eq` trait for equality: ```rust @@ -103,4 +103,4 @@ impl Eq for MyStruct { } ``` -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.30.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.30.0/noir/concepts/unconstrained.md index b8e71fe65f0..96f824c5e42 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/concepts/unconstrained.md +++ b/docs/versioned_docs/version-v0.30.0/noir/concepts/unconstrained.md @@ -96,4 +96,4 @@ Generally we want to use brillig whenever there's something that's easy to verif ## Break and Continue -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.30.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.30.0/noir/modules_packages_crates/dependencies.md index 04c1703d929..2c028d85853 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/modules_packages_crates/dependencies.md +++ b/docs/versioned_docs/version-v0.30.0/noir/modules_packages_crates/dependencies.md @@ -82,14 +82,14 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust use dep::std::ec::tecurve::affine::{Curve, Point}; ``` -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.30.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.30.0/noir/standard_library/black_box_fns.md index eeead580969..d5694250f05 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.30.0/noir/standard_library/black_box_fns.md @@ -25,7 +25,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion) +- [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.30.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.30.0/noir/standard_library/recursion.md index a93894043dc..f33c285cf4e 100644 --- a/docs/versioned_docs/version-v0.30.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.30.0/noir/standard_library/recursion.md @@ -35,7 +35,7 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F :::info -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. ::: diff --git a/docs/versioned_docs/version-v0.30.0/tooling/debugger.md b/docs/versioned_docs/version-v0.30.0/tooling/debugger.md index 7c158d949d1..9b7565ba9ff 100644 --- a/docs/versioned_docs/version-v0.30.0/tooling/debugger.md +++ b/docs/versioned_docs/version-v0.30.0/tooling/debugger.md @@ -12,7 +12,7 @@ There are currently two ways of debugging Noir programs: 1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). 2. Via the REPL debugger, which ships with Nargo. -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - Noir & Nargo ≥0.28.0 - Noir's VS Code extension ≥0.0.11 From 87a1d8ebaadb5f0f1ed637b96816f971f946af87 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 12 Jun 2024 09:22:45 -0500 Subject: [PATCH 18/57] fix(elaborator): Fix duplicate methods error (#5225) # Description ## Problem\* Resolves "duplicate method" error when declaring trait methods on different traits with the same method name in the elaborator. ## Summary\* ## Additional Context This was the last error type in aztec packages for the elaborator. ~~After this, there are still some extra unused variable warnings I will fix but the entire crate tests successfully.~~ Nevermind, looks like aztec-nr just naturally has many unused variables ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/elaborator/mod.rs | 47 ++++++++++++------- compiler/noirc_frontend/src/node_interner.rs | 1 + .../no_duplicate_methods/Nargo.toml | 6 +++ .../no_duplicate_methods/Prover.toml | 0 .../no_duplicate_methods/src/main.nr | 26 ++++++++++ 5 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 test_programs/compile_success_empty/no_duplicate_methods/Nargo.toml create mode 100644 test_programs/compile_success_empty/no_duplicate_methods/Prover.toml create mode 100644 test_programs/compile_success_empty/no_duplicate_methods/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 37ad74b78b0..aee3b5c5807 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -232,23 +232,20 @@ impl<'context> Elaborator<'context> { // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); - // Bind trait impls to their trait. Collect trait functions, that have a - // default implementation, which hasn't been overridden. - for trait_impl in &mut items.trait_impls { - this.collect_trait_impl(trait_impl); - } - // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be // done during def collection since we need to be able to resolve the type of // the impl since that determines the module we should collect into. - // - // These are resolved after trait impls so that struct methods are chosen - // over trait methods if there are name conflicts. for ((_self_type, module), impls) in &mut items.impls { this.collect_impls(*module, impls); } + // Bind trait impls to their trait. Collect trait functions, that have a + // default implementation, which hasn't been overridden. + for trait_impl in &mut items.trait_impls { + this.collect_trait_impl(trait_impl); + } + // We must wait to resolve non-literal globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to to ensure they are valid. while let Some((_, global)) = this.unresolved_globals.pop_first() { @@ -860,9 +857,12 @@ impl<'context> Elaborator<'context> { self.self_type = None; } - fn get_module_mut(&mut self, module: ModuleId) -> &mut ModuleData { + fn get_module_mut( + def_maps: &mut BTreeMap, + module: ModuleId, + ) -> &mut ModuleData { let message = "A crate should always be present for a given crate id"; - &mut self.def_maps.get_mut(&module.krate).expect(message).modules[module.local_id.0] + &mut def_maps.get_mut(&module.krate).expect(message).modules[module.local_id.0] } fn declare_methods_on_struct( @@ -890,7 +890,7 @@ impl<'context> Elaborator<'context> { // Grab the module defined by the struct type. Note that impls are a case // where the module the methods are added to is not the same as the module // they are resolved in. - let module = self.get_module_mut(struct_ref.id.module_id()); + let module = Self::get_module_mut(self.def_maps, struct_ref.id.module_id()); for (_, method_id, method) in &functions.functions { // If this method was already declared, remove it from the module so it cannot @@ -899,22 +899,37 @@ impl<'context> Elaborator<'context> { // If not, that is specialization which is allowed. let name = method.name_ident().clone(); if module.declare_function(name, ItemVisibility::Public, *method_id).is_err() { - module.remove_function(method.name_ident()); + let existing = module.find_func_with_name(method.name_ident()).expect( + "declare_function should only error if there is an existing function", + ); + + // Only remove the existing function from scope if it is from a trait impl as + // well. If it is from a non-trait impl that should override trait impl methods + // anyway so that Foo::bar always resolves to the non-trait impl version. + if self.interner.function_meta(&existing).trait_impl.is_some() { + module.remove_function(method.name_ident()); + } } } - self.declare_struct_methods(self_type, &function_ids); + // Trait impl methods are already declared in NodeInterner::add_trait_implementation + if !is_trait_impl { + self.declare_methods(self_type, &function_ids); + } // We can define methods on primitive types only if we're in the stdlib } else if !is_trait_impl && *self_type != Type::Error { if self.crate_id.is_stdlib() { - self.declare_struct_methods(self_type, &function_ids); + // Trait impl methods are already declared in NodeInterner::add_trait_implementation + if !is_trait_impl { + self.declare_methods(self_type, &function_ids); + } } else { self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { span }); } } } - fn declare_struct_methods(&mut self, self_type: &Type, function_ids: &[FuncId]) { + fn declare_methods(&mut self, self_type: &Type, function_ids: &[FuncId]) { for method_id in function_ids { let method_name = self.interner.function_name(method_id).to_owned(); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index cef49332b00..6c5f9b6bbcb 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1466,6 +1466,7 @@ impl NodeInterner { force_type_check: bool, ) -> Option { let methods = self.struct_methods.get(&(id, method_name.to_owned())); + // If there is only one method, just return it immediately. // It will still be typechecked later. if !force_type_check { diff --git a/test_programs/compile_success_empty/no_duplicate_methods/Nargo.toml b/test_programs/compile_success_empty/no_duplicate_methods/Nargo.toml new file mode 100644 index 00000000000..2125d475530 --- /dev/null +++ b/test_programs/compile_success_empty/no_duplicate_methods/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "no_duplicate_methods" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/no_duplicate_methods/Prover.toml b/test_programs/compile_success_empty/no_duplicate_methods/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr b/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr new file mode 100644 index 00000000000..2be1d3fa11e --- /dev/null +++ b/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr @@ -0,0 +1,26 @@ +// Test that declaring several methods & trait methods with the same name +// does not trigger a duplicate method error +trait ToField { + fn to_field(self) -> Field; +} +trait ToField2 { + fn to_field(self) -> Field; +} + +struct Foo { x: Field } + +impl ToField for Foo { + fn to_field(self) -> Field { self.x } +} + +impl ToField2 for Foo { + fn to_field(self) -> Field { self.x } +} + +impl Foo { + fn to_field(self) -> Field { + self.x + } +} + +fn main() {} From fde432aacc436b6c57f0d937d7c86836bac0b465 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 12 Jun 2024 10:45:58 -0500 Subject: [PATCH 19/57] fix(elaborator): Fix regression introduced by lazy-global changes (#5223) # Description ## Problem\* Fixes a regression introduced by #5191 ## Summary\* When a global is lazily evaluated, the local module id and file id of the elaborator would change without being reset to the previous value. This lead to scoping issues after the global was evaluated. ## Additional Context I was unable to create a repro for this aside from running `nargo t --use-elaborator` on `aztec-nr` unfortunately. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/elaborator/mod.rs | 15 ++++++++++++--- .../noirc_frontend/src/hir/resolution/errors.rs | 2 +- .../noirc_frontend/src/hir/resolution/traits.rs | 1 + compiler/noirc_frontend/src/node_interner.rs | 7 +++++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index aee3b5c5807..3c88a419b99 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -308,6 +308,7 @@ impl<'context> Elaborator<'context> { let old_function = std::mem::replace(&mut self.current_function, Some(id)); // Without this, impl methods can accidentally be placed in contracts. See #3254 + let was_in_contract = self.in_contract; if self.self_type.is_some() { self.in_contract = false; } @@ -403,6 +404,7 @@ impl<'context> Elaborator<'context> { self.trait_bounds.clear(); self.in_unconstrained_fn = false; self.interner.update_fn(id, hir_func); + self.in_contract = was_in_contract; self.current_function = old_function; self.current_item = old_item; } @@ -544,6 +546,7 @@ impl<'context> Elaborator<'context> { self.current_function = Some(func_id); // Without this, impl methods can accidentally be placed in contracts. See #3254 + let was_in_contract = self.in_contract; if self.self_type.is_some() { self.in_contract = false; } @@ -659,6 +662,7 @@ impl<'context> Elaborator<'context> { }; self.interner.push_fn_meta(meta, func_id); + self.in_contract = was_in_contract; self.current_function = None; self.scopes.end_function(); self.current_item = None; @@ -1095,6 +1099,7 @@ impl<'context> Elaborator<'context> { // make sure every struct's fields is accurately set. for id in struct_ids { let struct_type = self.interner.get_struct(id); + // Only handle structs without generics as any generics args will be checked // after monomorphization when performing SSA codegen if struct_type.borrow().generics.is_empty() { @@ -1129,8 +1134,8 @@ impl<'context> Elaborator<'context> { } fn elaborate_global(&mut self, global: UnresolvedGlobal) { - self.local_module = global.module_id; - self.file = global.file_id; + let old_module = std::mem::replace(&mut self.local_module, global.module_id); + let old_file = std::mem::replace(&mut self.file, global.file_id); let global_id = global.global_id; self.current_item = Some(DependencyId::Global(global_id)); @@ -1162,6 +1167,8 @@ impl<'context> Elaborator<'context> { // Otherwise we may prematurely default to a Field inside the next function if this // global was unused there, even if it is consistently used as a u8 everywhere else. self.type_variables.clear(); + self.local_module = old_module; + self.file = old_file; } fn elaborate_comptime_global(&mut self, global_id: GlobalId) { @@ -1201,11 +1208,13 @@ impl<'context> Elaborator<'context> { self.local_module = *local_module; for (generics, _, function_set) in function_sets { + self.file = function_set.file_id; self.add_generics(generics); let self_type = self.resolve_type(self_type.clone()); function_set.self_type = Some(self_type.clone()); self.self_type = Some(self_type); self.define_function_metas_for_functions(function_set); + self.self_type = None; self.generics.clear(); } } @@ -1220,10 +1229,10 @@ impl<'context> Elaborator<'context> { let trait_generics = vecmap(&trait_impl.trait_generics, |generic| self.resolve_type(generic.clone())); + trait_impl.resolved_trait_generics = trait_generics; let self_type = self.resolve_type(unresolved_type.clone()); - self.self_type = Some(self_type.clone()); trait_impl.methods.self_type = Some(self_type); diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 06f6dda7142..07fbde4db8e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -319,7 +319,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), ident.span(), ), - ResolverError::OracleMarkedAsConstrained { ident } => Diagnostic::simple_error( + ResolverError::OracleMarkedAsConstrained { ident } => Diagnostic::simple_warning( error.to_string(), "Oracle functions must have the `unconstrained` keyword applied".into(), ident.span(), diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 3d355fd4447..4c360731711 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -300,6 +300,7 @@ fn collect_trait_impl( let file = def_maps[&crate_id].file_id(trait_impl.module_id); let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); resolver.add_generics(&trait_impl.generics); + let typ = resolver.resolve_type(unresolved_type); errors.extend(take_errors(trait_impl.file_id, resolver)); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 6c5f9b6bbcb..e29c3183993 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1411,6 +1411,13 @@ impl NodeInterner { ) -> Result<(), (Span, FileId)> { self.trait_implementations.insert(impl_id, trait_impl.clone()); + // Avoid adding error types to impls since they'll conflict with every other type. + // We don't need to return an error since we expect an error to already be issued when + // the error type is created. + if object_type == Type::Error { + return Ok(()); + } + // Replace each generic with a fresh type variable let substitutions = impl_generics .into_iter() From 802cd951d90bfdb44e7a6b435ab852369c3a6f8d Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 12 Jun 2024 14:34:32 -0500 Subject: [PATCH 20/57] chore: Optimize the elaborator (#5230) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5194 ## Summary\* Fixes a performance issue where the elaborator was ~2x as slow running `nargo t --use-elaborator` on aztec-nr versus the same tests without the elaborator (4s versus 2s). Found via flamegraph - `self.type_variables` wasn't cleared after each function was elaborated. This Vec is meant to just store type variables local to a function so that we can check if any need type hints after it is done but since it wasn't cleared out, we'd be checking an increasingly large Vec for each function. ## Additional Context With this change the elaborator is slightly faster (but not noticeably) than our current code. 1.9 versus 2.0s average across 10 runs for `nargo t` (with lower std. deviation than our current code as well). ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/elaborator/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 3c88a419b99..3b8158667f9 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -402,6 +402,7 @@ impl<'context> Elaborator<'context> { meta.function_body = FunctionBody::Resolved; self.trait_bounds.clear(); + self.type_variables.clear(); self.in_unconstrained_fn = false; self.interner.update_fn(id, hir_func); self.in_contract = was_in_contract; From 939bad2b20f0ca884d9a1ccbcee1ba3451621458 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 12 Jun 2024 14:56:47 -0500 Subject: [PATCH 21/57] chore: Parse macros (#5229) # Description ## Problem\* ## Summary\* To try to make it easier to review I'm breaking the parser related changes here out #5203. ## Additional Context It turns out `parser/types.rs` was never in our module tree. I've added it and removed the duplicate parsers for parsing types that were in `parser/parser.rs`. They've had some changes since the copies in `parser/types.rs` were first changed so I've updated those as well. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- aztec_macros/src/utils/ast_utils.rs | 7 +- compiler/noirc_frontend/src/ast/expression.rs | 41 ++- compiler/noirc_frontend/src/ast/mod.rs | 12 +- compiler/noirc_frontend/src/ast/statement.rs | 1 + compiler/noirc_frontend/src/debug/mod.rs | 4 + .../src/elaborator/expressions.rs | 2 +- .../src/hir/resolution/resolver.rs | 2 +- compiler/noirc_frontend/src/lexer/lexer.rs | 1 + compiler/noirc_frontend/src/lexer/token.rs | 9 + compiler/noirc_frontend/src/parser/parser.rs | 283 +++--------------- .../noirc_frontend/src/parser/parser/types.rs | 139 ++++++--- tooling/nargo_fmt/src/rewrite/expr.rs | 10 +- tooling/nargo_fmt/tests/expected/let.nr | 4 +- tooling/nargo_fmt/tests/input/let.nr | 4 +- 14 files changed, 220 insertions(+), 299 deletions(-) diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 4706be2df25..48b3b25747b 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -47,12 +47,17 @@ pub fn method_call( object, method_name: ident(method_name), arguments, + is_macro_call: false, generics: None, }))) } pub fn call(func: Expression, arguments: Vec) -> Expression { - expression(ExpressionKind::Call(Box::new(CallExpression { func: Box::new(func), arguments }))) + expression(ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(func), + is_macro_call: false, + arguments, + }))) } pub fn pattern(name: &str) -> Pattern { diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 50836add8de..da2d3646b20 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -33,7 +33,7 @@ pub enum ExpressionKind { Tuple(Vec), Lambda(Box), Parenthesized(Box), - Quote(BlockExpression), + Quote(BlockExpression, Span), Comptime(BlockExpression, Span), // This variant is only emitted when inlining the result of comptime @@ -179,19 +179,21 @@ impl Expression { pub fn member_access_or_method_call( lhs: Expression, - (rhs, args): UnaryRhsMemberAccess, + rhs: UnaryRhsMemberAccess, span: Span, ) -> Expression { - let kind = match args { - None => ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs, rhs })), - Some((generics, arguments)) => { - ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: lhs, - method_name: rhs, - generics, - arguments, - })) + let kind = match rhs.method_call { + None => { + let rhs = rhs.method_or_field; + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs, rhs })) } + Some(method_call) => ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: lhs, + method_name: rhs.method_or_field, + generics: method_call.turbofish, + arguments: method_call.args, + is_macro_call: method_call.macro_call, + })), }; Expression::new(kind, span) } @@ -206,7 +208,12 @@ impl Expression { Expression::new(kind, span) } - pub fn call(lhs: Expression, arguments: Vec, span: Span) -> Expression { + pub fn call( + lhs: Expression, + is_macro_call: bool, + arguments: Vec, + span: Span, + ) -> Expression { // Need to check if lhs is an if expression since users can sequence if expressions // with tuples without calling them. E.g. `if c { t } else { e }(a, b)` is interpreted // as a sequence of { if, tuple } rather than a function call. This behavior matches rust. @@ -224,7 +231,11 @@ impl Expression { ], }) } else { - ExpressionKind::Call(Box::new(CallExpression { func: Box::new(lhs), arguments })) + ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(lhs), + is_macro_call, + arguments, + })) }; Expression::new(kind, span) } @@ -447,6 +458,7 @@ pub enum ArrayLiteral { pub struct CallExpression { pub func: Box, pub arguments: Vec, + pub is_macro_call: bool, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -456,6 +468,7 @@ pub struct MethodCallExpression { /// Method calls have an optional list of generics if the turbofish operator was used pub generics: Option>, pub arguments: Vec, + pub is_macro_call: bool, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -535,7 +548,7 @@ impl Display for ExpressionKind { } Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), - Quote(block) => write!(f, "quote {block}"), + Quote(block, _) => write!(f, "quote {block}"), Comptime(block, _) => write!(f, "comptime {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index bd2b45d9c48..c09ad75818a 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -134,8 +134,16 @@ pub struct UnresolvedType { } /// Type wrapper for a member access -pub(crate) type UnaryRhsMemberAccess = - (Ident, Option<(Option>, Vec)>); +pub struct UnaryRhsMemberAccess { + pub method_or_field: Ident, + pub method_call: Option, +} + +pub struct UnaryRhsMethodCall { + pub turbofish: Option>, + pub macro_call: bool, + pub args: Vec, +} /// The precursor to TypeExpression, this is the type that the parser allows /// to be used in the length position of an array type. Only constants, variables, diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 9b2c0fbfee8..57a62324064 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -608,6 +608,7 @@ impl ForRange { object: Expression::new(array_ident.clone(), array_span), method_name: Ident::new("len".to_string(), array_span), generics: None, + is_macro_call: false, arguments: vec![], })); let end_range = Expression::new(end_range, array_span); diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index c222e08e77a..f5866f5b756 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -581,6 +581,7 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta ), span, }), + is_macro_call: false, arguments: vec![uint_expr(var_id.0 as u128, span), expr], })); ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } @@ -599,6 +600,7 @@ fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement { ), span, }), + is_macro_call: false, arguments: vec![uint_expr(var_id.0 as u128, span)], })); ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } @@ -626,6 +628,7 @@ fn build_assign_member_stmt( ), span, }), + is_macro_call: false, arguments: [ vec![uint_expr(var_id.0 as u128, span)], vec![expr.clone()], @@ -649,6 +652,7 @@ fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Stat ), span, }), + is_macro_call: false, arguments: vec![uint_expr(fn_id.0 as u128, span)], })); ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index a922f552c4b..c1c6945707b 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -58,7 +58,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), - ExpressionKind::Quote(quote) => self.elaborate_quote(quote), + ExpressionKind::Quote(quote, _) => self.elaborate_quote(quote), ExpressionKind::Comptime(comptime, _) => { return self.elaborate_comptime_block(comptime, expr.span) } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 01f58ba4c27..133f971da19 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1641,7 +1641,7 @@ impl<'a> Resolver<'a> { ExpressionKind::Parenthesized(sub_expr) => return self.resolve_expression(*sub_expr), // The quoted expression isn't resolved since we don't want errors if variables aren't defined - ExpressionKind::Quote(block) => HirExpression::Quote(block), + ExpressionKind::Quote(block, _) => HirExpression::Quote(block), ExpressionKind::Comptime(block, _) => { HirExpression::Comptime(self.resolve_block(block)) } diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index 2d1ebf530e3..3d052e22e36 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -141,6 +141,7 @@ impl<'a> Lexer<'a> { Some('}') => self.single_char_token(Token::RightBrace), Some('[') => self.single_char_token(Token::LeftBracket), Some(']') => self.single_char_token(Token::RightBracket), + Some('$') => self.single_char_token(Token::DollarSign), Some('"') => self.eat_string_literal(), Some('f') => self.eat_format_string_or_alpha_numeric(), Some('r') => self.eat_raw_string_or_alpha_numeric(), diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index d8555b4fbf7..2199333e90f 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -85,6 +85,8 @@ pub enum BorrowedToken<'input> { Semicolon, /// ! Bang, + /// $ + DollarSign, /// = Assign, #[allow(clippy::upper_case_acronyms)] @@ -179,6 +181,8 @@ pub enum Token { Bang, /// = Assign, + /// $ + DollarSign, #[allow(clippy::upper_case_acronyms)] EOF, @@ -238,6 +242,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::Semicolon => BorrowedToken::Semicolon, Token::Assign => BorrowedToken::Assign, Token::Bang => BorrowedToken::Bang, + Token::DollarSign => BorrowedToken::DollarSign, Token::EOF => BorrowedToken::EOF, Token::Invalid(c) => BorrowedToken::Invalid(*c), Token::Whitespace(ref s) => BorrowedToken::Whitespace(s), @@ -349,6 +354,7 @@ impl fmt::Display for Token { Token::Semicolon => write!(f, ";"), Token::Assign => write!(f, "="), Token::Bang => write!(f, "!"), + Token::DollarSign => write!(f, "$"), Token::EOF => write!(f, "end of input"), Token::Invalid(c) => write!(f, "{c}"), Token::Whitespace(ref s) => write!(f, "{s}"), @@ -840,6 +846,7 @@ pub enum Keyword { Crate, Dep, Else, + Expr, Field, Fn, For, @@ -884,6 +891,7 @@ impl fmt::Display for Keyword { Keyword::Crate => write!(f, "crate"), Keyword::Dep => write!(f, "dep"), Keyword::Else => write!(f, "else"), + Keyword::Expr => write!(f, "Expr"), Keyword::Field => write!(f, "Field"), Keyword::Fn => write!(f, "fn"), Keyword::For => write!(f, "for"), @@ -931,6 +939,7 @@ impl Keyword { "crate" => Keyword::Crate, "dep" => Keyword::Dep, "else" => Keyword::Else, + "Expr" => Keyword::Expr, "Field" => Keyword::Field, "fn" => Keyword::Fn, "for" => Keyword::For, diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index cabc788e07d..5107e10f4df 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -24,6 +24,7 @@ //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. use self::primitives::{keyword, mutable_reference, variable}; +use self::types::{generic_type_args, maybe_comp_time, parse_type}; use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, @@ -35,8 +36,8 @@ use super::{spanned, Item, ItemKind}; use crate::ast::{ BinaryOp, BinaryOpKind, BlockExpression, ForLoopStatement, ForRange, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, NoirTypeAlias, Param, Path, Pattern, - Recoverable, Statement, TraitBound, TypeImpl, UnaryRhsMemberAccess, UnresolvedTraitConstraint, - UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, + Recoverable, Statement, TraitBound, TypeImpl, UnaryRhsMemberAccess, UnaryRhsMethodCall, + UnresolvedTraitConstraint, UseTree, UseTreeKind, Visibility, }; use crate::ast::{ Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, @@ -59,6 +60,7 @@ mod path; mod primitives; mod structs; mod traits; +mod types; // synthesized by LALRPOP lalrpop_mod!(pub noir_parser); @@ -674,41 +676,6 @@ where }) } -fn parse_type<'a>() -> impl NoirParser + 'a { - recursive(parse_type_inner) -} - -fn parse_type_inner<'a>( - recursive_type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - choice(( - field_type(), - int_type(), - bool_type(), - string_type(), - format_string_type(recursive_type_parser.clone()), - named_type(recursive_type_parser.clone()), - named_trait(recursive_type_parser.clone()), - slice_type(recursive_type_parser.clone()), - array_type(recursive_type_parser.clone()), - parenthesized_type(recursive_type_parser.clone()), - tuple_type(recursive_type_parser.clone()), - function_type(recursive_type_parser.clone()), - mutable_reference_type(recursive_type_parser), - )) -} - -fn parenthesized_type( - recursive_type_parser: impl NoirParser, -) -> impl NoirParser { - recursive_type_parser - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(|typ, span| UnresolvedType { - typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), - span: span.into(), - }) -} - fn optional_visibility() -> impl NoirParser { keyword(Keyword::Pub) .or(keyword(Keyword::CallData)) @@ -724,187 +691,6 @@ fn optional_visibility() -> impl NoirParser { }) } -fn maybe_comp_time() -> impl NoirParser { - keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { - if opt.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Comptime values"), - span, - )); - } - opt.is_some() - }) -} - -fn field_type() -> impl NoirParser { - keyword(Keyword::Field) - .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) -} - -fn bool_type() -> impl NoirParser { - keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) -} - -fn string_type() -> impl NoirParser { - keyword(Keyword::String) - .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) - .map_with_span(|expr, span| UnresolvedTypeData::String(expr).with_span(span)) -} - -fn format_string_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - keyword(Keyword::FormatString) - .ignore_then( - type_expression() - .then_ignore(just(Token::Comma)) - .then(type_parser) - .delimited_by(just(Token::Less), just(Token::Greater)), - ) - .map_with_span(|(size, fields), span| { - UnresolvedTypeData::FormatString(size, Box::new(fields)).with_span(span) - }) -} - -fn int_type() -> impl NoirParser { - filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - }) - .validate(|token, span, emit| { - UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( - |err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }, - ) - }) -} - -fn named_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - path().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { - UnresolvedTypeData::Named(path, args, false).with_span(span) - }) -} - -fn named_trait<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - keyword(Keyword::Impl).ignore_then(path()).then(generic_type_args(type_parser)).map_with_span( - |(path, args), span| UnresolvedTypeData::TraitAsType(path, args).with_span(span), - ) -} - -fn generic_type_args<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser> + 'a { - type_parser - .clone() - // Without checking for a terminating ',' or '>' here we may incorrectly - // parse a generic `N * 2` as just the type `N` then fail when there is no - // separator afterward. Failing early here ensures we try the `type_expression` - // parser afterward. - .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) - .or(type_expression() - .map_with_span(|expr, span| UnresolvedTypeData::Expression(expr).with_span(span))) - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1) - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not() - .map(Option::unwrap_or_default) -} - -fn array_type<'a>( - type_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - just(Token::LeftBracket) - .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression())) - .then_ignore(just(Token::RightBracket)) - .map_with_span(|(element_type, size), span| { - UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) - }) -} - -fn slice_type(type_parser: impl NoirParser) -> impl NoirParser { - just(Token::LeftBracket) - .ignore_then(type_parser) - .then_ignore(just(Token::RightBracket)) - .map_with_span(|element_type, span| { - UnresolvedTypeData::Slice(Box::new(element_type)).with_span(span) - }) -} - -fn type_expression() -> impl NoirParser { - recursive(|expr| { - expression_with_precedence( - Precedence::lowest_type_precedence(), - expr, - nothing(), - nothing(), - true, - false, - ) - }) - .labelled(ParsingRuleLabel::TypeExpression) - .try_map(UnresolvedTypeExpression::from_expr) -} - -fn tuple_type(type_parser: T) -> impl NoirParser -where - T: NoirParser, -{ - let fields = type_parser.separated_by(just(Token::Comma)).allow_trailing(); - parenthesized(fields).map_with_span(|fields, span| { - if fields.is_empty() { - UnresolvedTypeData::Unit.with_span(span) - } else { - UnresolvedTypeData::Tuple(fields).with_span(span) - } - }) -} - -fn function_type(type_parser: T) -> impl NoirParser -where - T: NoirParser, -{ - let args = parenthesized(type_parser.clone().separated_by(just(Token::Comma)).allow_trailing()); - - let env = just(Token::LeftBracket) - .ignore_then(type_parser.clone()) - .then_ignore(just(Token::RightBracket)) - .or_not() - .map_with_span(|t, span| { - t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(Span::empty(span.end()))) - }); - - keyword(Keyword::Fn) - .ignore_then(env) - .then(args) - .then_ignore(just(Token::Arrow)) - .then(type_parser) - .map_with_span(|((env, args), ret), span| { - UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env)).with_span(span) - }) -} - -fn mutable_reference_type(type_parser: T) -> impl NoirParser -where - T: NoirParser, -{ - just(Token::Ampersand) - .ignore_then(keyword(Keyword::Mut)) - .ignore_then(type_parser) - .map_with_span(|element, span| { - UnresolvedTypeData::MutableReference(Box::new(element)).with_span(span) - }) -} - fn expression() -> impl ExprParser { recursive(|expr| { expression_with_precedence( @@ -1073,14 +859,18 @@ where S: NoirParser + 'a, { enum UnaryRhs { - Call(Vec), + Call((Option, Vec)), ArrayIndex(Expression), Cast(UnresolvedType), MemberAccess(UnaryRhsMemberAccess), } // `(arg1, ..., argN)` in `my_func(arg1, ..., argN)` - let call_rhs = parenthesized(expression_list(expr_parser.clone())).map(UnaryRhs::Call); + // Optionally accepts a leading `!` for macro calls. + let call_rhs = just(Token::Bang) + .or_not() + .then(parenthesized(expression_list(expr_parser.clone()))) + .map(UnaryRhs::Call); // `[expr]` in `arr[expr]` let array_rhs = expr_parser @@ -1097,11 +887,23 @@ where // A turbofish operator is optional in a method call to specify generic types let turbofish = primitives::turbofish(type_parser); + // `::!(arg1, .., argN)` with the turbofish and macro portions being optional. + let method_call_rhs = turbofish + .then(just(Token::Bang).or_not()) + .then(parenthesized(expression_list(expr_parser.clone()))) + .map(|((turbofish, macro_call), args)| UnaryRhsMethodCall { + turbofish, + macro_call: macro_call.is_some(), + args, + }); + // `.foo` or `.foo(args)` in `atom.foo` or `atom.foo(args)` let member_rhs = just(Token::Dot) .ignore_then(field_name()) - .then(turbofish.then(parenthesized(expression_list(expr_parser.clone()))).or_not()) - .map(UnaryRhs::MemberAccess) + .then(method_call_rhs.or_not()) + .map(|(method_or_field, method_call)| { + UnaryRhs::MemberAccess(UnaryRhsMemberAccess { method_or_field, method_call }) + }) .labelled(ParsingRuleLabel::FieldAccess); let rhs = choice((call_rhs, array_rhs, cast_rhs, member_rhs)); @@ -1110,7 +912,9 @@ where atom(expr_parser, expr_no_constructors, statement, allow_constructors), rhs, |lhs, rhs, span| match rhs { - UnaryRhs::Call(args) => Expression::call(lhs, args, span), + UnaryRhs::Call((is_macro, args)) => { + Expression::call(lhs, is_macro.is_some(), args, span) + } UnaryRhs::ArrayIndex(index) => Expression::index(lhs, index, span), UnaryRhs::Cast(r#type) => Expression::cast(lhs, r#type, span), UnaryRhs::MemberAccess(field) => { @@ -1272,6 +1076,7 @@ where block(statement.clone()).map(ExpressionKind::Block), comptime_expr(statement.clone()), quote(statement), + unquote(expr_parser.clone()), variable(), literal(), )) @@ -1300,13 +1105,26 @@ fn quote<'a, P>(statement: P) -> impl NoirParser + 'a where P: NoirParser + 'a, { - keyword(Keyword::Quote).ignore_then(block(statement)).validate(|block, span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("quoted expressions"), - span, - )); - ExpressionKind::Quote(block) - }) + keyword(Keyword::Quote).ignore_then(spanned(block(statement))).validate( + |(block, block_span), span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("quoted expressions"), + span, + )); + ExpressionKind::Quote(block, block_span) + }, + ) +} + +/// unquote: '$' variable +/// | '$' '(' expression ')' +fn unquote<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + let unquote = variable().map_with_span(Expression::new).or(parenthesized(expr_parser)); + // This will be updated to ExpressionKind::Unquote in a later PR + just(Token::DollarSign).ignore_then(unquote).map(|_| ExpressionKind::Error) } fn tuple

(expr_parser: P) -> impl NoirParser @@ -1452,11 +1270,6 @@ mod test { ); } - #[test] - fn parse_type_expression() { - parse_all(type_expression(), vec!["(123)", "123", "(1 + 1)", "(1 + (1))"]); - } - #[test] fn parse_array_sugar() { let valid = vec!["[0;7]", "[(1, 2); 4]", "[0;Four]", "[2;1+3-a]"]; diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 82dd3dad681..a79a2ef67f2 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,22 +1,38 @@ use super::{ - expression_with_precedence, keyword, nothing, parenthesized, NoirParser, ParserError, + expression_with_precedence, keyword, nothing, parenthesized, path, NoirParser, ParserError, ParserErrorReason, Precedence, }; -use crate::ast::{UnresolvedType, UnresolvedTypeData}; +use crate::ast::{Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; use crate::parser::labels::ParsingRuleLabel; use crate::token::{Keyword, Token}; -use crate::{Recoverable, UnresolvedTypeExpression}; use chumsky::prelude::*; use noirc_errors::Span; -fn maybe_comp_time() -> impl NoirParser<()> { - keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { - if opt.is_some() { - emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); - } - }) +pub(super) fn parse_type<'a>() -> impl NoirParser + 'a { + recursive(parse_type_inner) +} + +pub(super) fn parse_type_inner<'a>( + recursive_type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + choice(( + field_type(), + int_type(), + bool_type(), + string_type(), + expr_type(), + format_string_type(recursive_type_parser.clone()), + named_type(recursive_type_parser.clone()), + named_trait(recursive_type_parser.clone()), + slice_type(recursive_type_parser.clone()), + array_type(recursive_type_parser.clone()), + parenthesized_type(recursive_type_parser.clone()), + tuple_type(recursive_type_parser.clone()), + function_type(recursive_type_parser.clone()), + mutable_reference_type(recursive_type_parser), + )) } pub(super) fn parenthesized_type( @@ -30,29 +46,41 @@ pub(super) fn parenthesized_type( }) } +pub(super) fn maybe_comp_time() -> impl NoirParser { + keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { + if opt.is_some() { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("Comptime values"), + span, + )); + } + opt.is_some() + }) +} + pub(super) fn field_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Field)) + keyword(Keyword::Field) .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) } pub(super) fn bool_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Bool)) - .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) + keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) +} + +/// This is the type `Expr` - the type of a quoted, untyped expression object used for macros +pub(super) fn expr_type() -> impl NoirParser { + keyword(Keyword::Expr).map_with_span(|_, span| UnresolvedTypeData::Code.with_span(span)) } pub(super) fn string_type() -> impl NoirParser { keyword(Keyword::String) - .ignore_then( - type_expression().delimited_by(just(Token::Less), just(Token::Greater)).or_not(), - ) + .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) .map_with_span(|expr, span| UnresolvedTypeData::String(expr).with_span(span)) } -pub(super) fn format_string_type( - type_parser: impl NoirParser, -) -> impl NoirParser { +pub(super) fn format_string_type<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { keyword(Keyword::FormatString) .ignore_then( type_expression() @@ -66,26 +94,61 @@ pub(super) fn format_string_type( } pub(super) fn int_type() -> impl NoirParser { - maybe_comp_time() - .then(filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - })) - .validate(|(_, token), span, emit| { - UnresolvedTypeData::from_int_token(token) - .map(|data| data.with_span(span)) - .unwrap_or_else(|err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }) - }) + filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + }) + .validate(|token, span, emit| { + UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( + |err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }, + ) + }) } -pub(super) fn array_type( - type_parser: impl NoirParser, -) -> impl NoirParser { +pub(super) fn named_type<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + path().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { + UnresolvedTypeData::Named(path, args, false).with_span(span) + }) +} + +pub(super) fn named_trait<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + keyword(Keyword::Impl).ignore_then(path()).then(generic_type_args(type_parser)).map_with_span( + |(path, args), span| UnresolvedTypeData::TraitAsType(path, args).with_span(span), + ) +} + +pub(super) fn generic_type_args<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser> + 'a { + type_parser + .clone() + // Without checking for a terminating ',' or '>' here we may incorrectly + // parse a generic `N * 2` as just the type `N` then fail when there is no + // separator afterward. Failing early here ensures we try the `type_expression` + // parser afterward. + .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) + .or(type_expression() + .map_with_span(|expr, span| UnresolvedTypeData::Expression(expr).with_span(span))) + .separated_by(just(Token::Comma)) + .allow_trailing() + .at_least(1) + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not() + .map(Option::unwrap_or_default) +} + +pub(super) fn array_type<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { just(Token::LeftBracket) .ignore_then(type_parser) .then(just(Token::Semicolon).ignore_then(type_expression())) diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 7ff943aea62..1d450bb7e28 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -63,7 +63,8 @@ pub(crate) fn rewrite( NewlineMode::IfContainsNewLineAndWidth, ); - format!("{callee}{args}") + let bang = if call_expr.is_macro_call { "!" } else { "" }; + format!("{callee}{bang}{args}") } ExpressionKind::MethodCall(method_call_expr) => { let args_span = visitor.span_before( @@ -85,7 +86,8 @@ pub(crate) fn rewrite( NewlineMode::IfContainsNewLineAndWidth, ); - format!("{object}.{method}{turbofish}{args}") + let bang = if method_call_expr.is_macro_call { "!" } else { "" }; + format!("{object}.{method}{turbofish}{bang}{args}") } ExpressionKind::MemberAccess(member_access_expr) => { let lhs_str = rewrite_sub_expr(visitor, shape, member_access_expr.lhs); @@ -166,7 +168,9 @@ pub(crate) fn rewrite( format!("{path_string}{turbofish}") } ExpressionKind::Lambda(_) => visitor.slice(span).to_string(), - ExpressionKind::Quote(block) => format!("quote {}", rewrite_block(visitor, block, span)), + ExpressionKind::Quote(block, block_span) => { + format!("quote {}", rewrite_block(visitor, block, block_span)) + } ExpressionKind::Comptime(block, block_span) => { format!("comptime {}", rewrite_block(visitor, block, block_span)) } diff --git a/tooling/nargo_fmt/tests/expected/let.nr b/tooling/nargo_fmt/tests/expected/let.nr index c57801155a0..7ff69e74306 100644 --- a/tooling/nargo_fmt/tests/expected/let.nr +++ b/tooling/nargo_fmt/tests/expected/let.nr @@ -44,12 +44,12 @@ fn let_() { } }; - let expr = Expr { + let expr = MyExpr { // A boolean literal (true, false). kind: ExprKind::Bool(true) }; - let expr = Expr { /*A boolean literal (true, false).*/ kind: ExprKind::Bool(true) }; + let expr = MyExpr { /*A boolean literal (true, false).*/ kind: ExprKind::Bool(true) }; let mut V = dep::crate2::MyStruct { Q: x }; let mut V = dep::crate2::MyStruct {}; diff --git a/tooling/nargo_fmt/tests/input/let.nr b/tooling/nargo_fmt/tests/input/let.nr index 67c4ab8bd52..37cdc6655c7 100644 --- a/tooling/nargo_fmt/tests/input/let.nr +++ b/tooling/nargo_fmt/tests/input/let.nr @@ -20,11 +20,11 @@ fn let_() { let person = Person { first_name: "John", last_name: "Doe", home_address: Address { street: "123 Main St", city: "Exampleville", zip_code: "12345", master: Person { first_name: "John", last_name: "Doe", home_address: Address { street: "123 Main St", city: "Exampleville", zip_code: "12345" } } } }; - let expr = Expr {// A boolean literal (true, false). + let expr = MyExpr {// A boolean literal (true, false). kind: ExprKind::Bool(true), }; - let expr = Expr {/*A boolean literal (true, false).*/kind: ExprKind::Bool(true),}; + let expr = MyExpr {/*A boolean literal (true, false).*/kind: ExprKind::Bool(true),}; let mut V = dep::crate2::MyStruct { Q: x }; let mut V = dep::crate2::MyStruct {}; From f846879dd038328bd0a1d39a72b448ef52a1002b Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:30:42 +0200 Subject: [PATCH 22/57] fix: add support for nested arrays returned by oracles (#5132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Resolves #4561 ## Summary\* Generates brillig arrays of arrays during brillig-gen for types having nested arrays. Populate these nested brillig arrays in the brillig VM when data returned from oracles does not match the size of the outer array ## Additional Context For this to work, it is required that the oracle returns flatten values, although its Noir signature is a nested type. It's certainly possible to deserialise nested json arrays but I could not get it done easily so it'll be better handled in a separate PR. ## Documentation\* Check one: - [ ] No documentation needed. - [X] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Álvaro Rodríguez Co-authored-by: TomAFrench --- acvm-repo/brillig_vm/src/lib.rs | 154 +++++++++++++----- .../src/brillig/brillig_gen/brillig_block.rs | 40 ++++- docs/docs/how_to/how-to-oracles.md | 4 + .../regression_4561/Nargo.toml | 6 + .../regression_4561/src/main.nr | 44 +++++ 5 files changed, 201 insertions(+), 47 deletions(-) create mode 100644 test_programs/noir_test_success/regression_4561/Nargo.toml create mode 100644 test_programs/noir_test_success/regression_4561/src/main.nr diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index da9a34f1044..862162ddccf 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -482,60 +482,64 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> VM<'a, F, B> { destinations.iter().zip(destination_value_types).zip(&values) { match (destination, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { - match output { - ForeignCallParam::Single(value) => { - self.write_value_to_memory(*value_index, value, *bit_size)?; - } - _ => return Err(format!( - "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") - ), + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { + match output { + ForeignCallParam::Single(value) => { + self.write_value_to_memory(*value_index, value, *bit_size)?; } + _ => return Err(format!( + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") + ), } - ( - ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), - HeapValueType::Array { value_types, size: type_size }, - ) if size == type_size => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - if values.len() != *size { - return Err("Foreign call result array doesn't match expected size".to_string()); - } - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - } - _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + } + ( + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), + HeapValueType::Array { value_types, size: type_size }, + ) if size == type_size => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + if values.len() != *size { + return Err("Foreign call result array doesn't match expected size".to_string()); } + self.write_values_to_memory_slice(*pointer_index, values, value_types)?; + } + _ => { + return Err("Function result size does not match brillig bytecode size".to_string()); } - } else { - unimplemented!("deflattening heap arrays from foreign calls"); } - } - ( - ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), - HeapValueType::Vector { value_types }, - ) => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - // Set our size in the size address - self.memory.write(*size_index, values.len().into()); - - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - } - _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); - } + } else { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; + } + } + ( + ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), + HeapValueType::Vector { value_types }, + ) => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + // Set our size in the size address + self.memory.write(*size_index, values.len().into()); + self.write_values_to_memory_slice(*pointer_index, values, value_types)?; + + } + _ => { + return Err("Function result size does not match brillig bytecode size".to_string()); } - } else { - unimplemented!("deflattening heap vectors from foreign calls"); } - } - _ => { - return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); + } else { + unimplemented!("deflattening heap vectors from foreign calls"); } } + _ => { + return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); + } + } } let _ = @@ -596,6 +600,66 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> VM<'a, F, B> { Ok(()) } + /// Writes flattened values to memory, using the provided type + /// Function calls itself recursively in order to work with recursive types (nested arrays) + /// values_idx is the current index in the values vector and is incremented every time + /// a value is written to memory + /// The function returns the address of the next value to be written + fn write_slice_of_values_to_memory( + &mut self, + destination: MemoryAddress, + values: &Vec, + values_idx: &mut usize, + value_type: &HeapValueType, + ) -> Result { + let mut current_pointer = destination; + match value_type { + HeapValueType::Simple(bit_size) => { + self.write_value_to_memory(destination, &values[*values_idx], *bit_size)?; + *values_idx += 1; + Ok(MemoryAddress(destination.to_usize() + 1)) + } + HeapValueType::Array { value_types, size } => { + for _ in 0..*size { + for typ in value_types { + match typ { + HeapValueType::Simple(len) => { + self.write_value_to_memory( + current_pointer, + &values[*values_idx], + *len, + )?; + *values_idx += 1; + current_pointer = MemoryAddress(current_pointer.to_usize() + 1); + } + HeapValueType::Array { .. } => { + let destination = self.memory.read_ref(current_pointer); + let destination = self.memory.read_ref(destination); + self.write_slice_of_values_to_memory( + destination, + values, + values_idx, + typ, + )?; + current_pointer = MemoryAddress(current_pointer.to_usize() + 1); + } + HeapValueType::Vector { .. } => { + return Err(format!( + "Unsupported returned type in foreign calls {:?}", + typ + )); + } + } + } + } + Ok(current_pointer) + } + HeapValueType::Vector { .. } => { + Err(format!("Unsupported returned type in foreign calls {:?}", value_type)) + } + } + } + /// Process a binary operation. /// This method will not modify the program counter. fn process_binary_field_op( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 1fa4f41b29c..f165427233e 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1737,8 +1737,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.brillig_context.codegen_allocate_fixed_length_array(array.pointer, array.size); - self.brillig_context.usize_const_instruction(array.rc, 1_usize.into()); + self.allocate_nested_array(typ, Some(array)); variable } @@ -1765,6 +1764,43 @@ impl<'block> BrilligBlock<'block> { } } + fn allocate_nested_array( + &mut self, + typ: &Type, + array: Option, + ) -> BrilligVariable { + match typ { + Type::Array(types, size) => { + let array = array.unwrap_or(BrilligArray { + pointer: self.brillig_context.allocate_register(), + size: *size, + rc: self.brillig_context.allocate_register(), + }); + self.brillig_context.codegen_allocate_fixed_length_array(array.pointer, array.size); + self.brillig_context.usize_const_instruction(array.rc, 1_usize.into()); + + let mut index = 0_usize; + for _ in 0..*size { + for element_type in types.iter() { + match element_type { + Type::Array(_, _) => { + let inner_array = self.allocate_nested_array(element_type, None); + let idx = + self.brillig_context.make_usize_constant_instruction(index.into()); + self.store_variable_in_array(array.pointer, idx, inner_array); + } + Type::Slice(_) => unreachable!("ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type"), + _ => (), + } + index += 1; + } + } + BrilligVariable::BrilligArray(array) + } + _ => unreachable!("ICE: allocate_nested_array() expects an array, got {typ:?}"), + } + } + /// Gets the "user-facing" length of an array. /// An array of structs with two fields would be stored as an 2 * array.len() array/vector. /// So we divide the length by the number of subitems in an item to get the user-facing length. diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 62ead1f534f..2d2ed5c94b9 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -166,6 +166,10 @@ interface ForeignCallResult { } ``` +::: Multidimensional Arrays + +If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in json as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. + ::: ## Step 3 - Usage with Nargo diff --git a/test_programs/noir_test_success/regression_4561/Nargo.toml b/test_programs/noir_test_success/regression_4561/Nargo.toml new file mode 100644 index 00000000000..90deee74640 --- /dev/null +++ b/test_programs/noir_test_success/regression_4561/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_4561" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/noir_test_success/regression_4561/src/main.nr b/test_programs/noir_test_success/regression_4561/src/main.nr new file mode 100644 index 00000000000..138f7e805b3 --- /dev/null +++ b/test_programs/noir_test_success/regression_4561/src/main.nr @@ -0,0 +1,44 @@ +// Regression test for issue #4561 +use dep::std::test::OracleMock; + +type TReturnElem = [Field; 3]; +type TReturn = [TReturnElem; 2]; + +#[oracle(simple_nested_return)] +unconstrained fn simple_nested_return_oracle() -> TReturn {} + +unconstrained fn simple_nested_return_unconstrained() -> TReturn { + simple_nested_return_oracle() +} + +#[test] +fn test_simple_nested_return() { + OracleMock::mock("simple_nested_return").returns([1, 2, 3, 4, 5, 6]); + assert_eq(simple_nested_return_unconstrained(), [[1, 2, 3], [4, 5, 6]]); +} + +#[oracle(nested_with_fields_return)] +unconstrained fn nested_with_fields_return_oracle() -> (Field, TReturn, Field) {} + +unconstrained fn nested_with_fields_return_unconstrained() -> (Field, TReturn, Field) { + nested_with_fields_return_oracle() +} + +#[test] +fn test_nested_with_fields_return() { + OracleMock::mock("nested_with_fields_return").returns((0, [1, 2, 3, 4, 5, 6], 7)); + assert_eq(nested_with_fields_return_unconstrained(), (0, [[1, 2, 3], [4, 5, 6]], 7)); +} + +#[oracle(two_nested_return)] +unconstrained fn two_nested_return_oracle() -> (Field, TReturn, Field, TReturn) {} + +unconstrained fn two_nested_return_unconstrained() -> (Field, TReturn, Field, TReturn) { + two_nested_return_oracle() +} + +#[test] +fn two_nested_return() { + OracleMock::mock("two_nested_return").returns((0, [1, 2, 3, 4, 5, 6], 7, [1, 2, 3, 4, 5, 6])); + assert_eq(two_nested_return_unconstrained(), (0, [[1, 2, 3], [4, 5, 6]], 7, [[1, 2, 3], [4, 5, 6]])); +} \ No newline at end of file From 4aff0cb7764dd519919d4e4a16de4f4b67719684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 13 Jun 2024 12:32:35 +0100 Subject: [PATCH 23/57] chore(docs): fixing trailing slash issue (#5233) --- docs/docs/noir/standard_library/recursion.md | 8 +++----- docs/docusaurus.config.ts | 1 + docs/src/components/Notes/_blackbox.mdx | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/docs/noir/standard_library/recursion.md b/docs/docs/noir/standard_library/recursion.md index f33c285cf4e..cb3e7a9fcdf 100644 --- a/docs/docs/noir/standard_library/recursion.md +++ b/docs/docs/noir/standard_library/recursion.md @@ -4,6 +4,8 @@ description: Learn about how to write recursive proofs in Noir. keywords: [recursion, recursive proofs, verification_key, verify_proof] --- +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) @@ -33,11 +35,7 @@ By incorporating this attribute directly in the circuit's definition, tooling li pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` -:::info - -This is a black box function. Read [this section](./black_box_fns.md) to learn more about black box functions in Noir. - -::: + ## Example usage diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 29f612b0109..f0c986f1c28 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -14,6 +14,7 @@ export default { favicon: 'img/favicon.ico', url: 'https://noir-lang.org', baseUrl: '/', + trailingSlash: true, onBrokenLinks: 'throw', onBrokenMarkdownLinks: 'throw', i18n: { diff --git a/docs/src/components/Notes/_blackbox.mdx b/docs/src/components/Notes/_blackbox.mdx index 9fe9b48fbff..226017072c8 100644 --- a/docs/src/components/Notes/_blackbox.mdx +++ b/docs/src/components/Notes/_blackbox.mdx @@ -1,5 +1,5 @@ :::info -This is a black box function. Read [this section](../black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](../../black_box_fns) to learn more about black box functions in Noir. -::: \ No newline at end of file +::: From 51506ffb30f72f8d1365165eefd693a5b7231721 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 13 Jun 2024 12:57:14 +0100 Subject: [PATCH 24/57] chore: copy across typo PR script from aztec-packages (#5235) # Description ## Problem\* Resolves ## Summary\* This PR pulls across the typo PR script from aztec-packages so we can use it here as well. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- scripts/redo-typo-pr.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 scripts/redo-typo-pr.sh diff --git a/scripts/redo-typo-pr.sh b/scripts/redo-typo-pr.sh new file mode 100755 index 00000000000..416be65a449 --- /dev/null +++ b/scripts/redo-typo-pr.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -eux + +# Configuration +ORIGINAL_PR_NUMBER=$1 +REPO='noir-lang/noir' +NEW_BRANCH="chore/typo-redo-$ORIGINAL_PR_NUMBER" +AUTHOR=`gh pr view $ORIGINAL_PR_NUMBER --json author --jq '.author.login'` + +# Step 1: Checkout the PR locally +echo "Checking out PR #$ORIGINAL_PR_NUMBER" +gh pr checkout $ORIGINAL_PR_NUMBER + +# Step 2: Create a new local branch +echo "Creating new local branch $NEW_BRANCH" +git checkout -b $NEW_BRANCH + +# Step 3: Push the new branch to GitHub +echo "Pushing new branch $NEW_BRANCH to GitHub" +git commit --amend --no-edit +git push origin $NEW_BRANCH + +# Step 4: create a new pull request +echo "Creating a new pull request for $NEW_BRANCH" +gh pr create --base master --head $NEW_BRANCH --title "chore: redo typo PR by $AUTHOR" --body "Thanks $AUTHOR for https://github.com/$REPO/pull/$ORIGINAL_PR_NUMBER. Our policy is to redo typo changes to dissuade metric farming. This is an automated script." + +# Step 5: Close the original PR +echo "Closing original PR #$ORIGINAL_PR_NUMBER" +gh pr close $ORIGINAL_PR_NUMBER + +echo "Script completed." From 85f40930b36ce35cfb12736ccaed273d96e1ac7d Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 13 Jun 2024 12:59:18 +0100 Subject: [PATCH 25/57] chore: thread generics through ACIR/brillig gen (#5120) # Description ## Problem\* Resolves ## Summary\* This PR makes various structs related to ACIR gen and brillig gen generic over the field. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen.rs | 7 +- .../brillig/brillig_gen/brillig_black_box.rs | 19 +- .../src/brillig/brillig_gen/brillig_block.rs | 6 +- .../brillig_gen/brillig_block_variables.rs | 13 +- .../brillig/brillig_gen/brillig_directive.rs | 15 +- .../src/brillig/brillig_gen/brillig_fn.rs | 2 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 4 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 24 ++- .../src/brillig/brillig_ir/artifact.rs | 26 +-- .../src/brillig/brillig_ir/codegen_binary.rs | 8 +- .../src/brillig/brillig_ir/codegen_calls.rs | 7 +- .../brillig_ir/codegen_control_flow.rs | 27 ++- .../brillig/brillig_ir/codegen_intrinsic.rs | 12 +- .../src/brillig/brillig_ir/codegen_memory.rs | 5 +- .../src/brillig/brillig_ir/codegen_stack.rs | 6 +- .../src/brillig/brillig_ir/debug_show.rs | 4 +- .../src/brillig/brillig_ir/entry_point.rs | 45 ++-- .../src/brillig/brillig_ir/instructions.rs | 38 ++-- .../src/brillig/brillig_ir/registers.rs | 2 +- compiler/noirc_evaluator/src/brillig/mod.rs | 11 +- compiler/noirc_evaluator/src/ssa.rs | 2 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 195 ++++++++---------- .../src/ssa/acir_gen/acir_ir/big_int.rs | 16 +- .../ssa/acir_gen/acir_ir/generated_acir.rs | 68 +++--- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 35 ++-- 25 files changed, 292 insertions(+), 305 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index 5576d673f1d..ee61a9d13d3 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -6,12 +6,17 @@ pub(crate) mod brillig_fn; pub(crate) mod brillig_slice_ops; mod variable_liveness; +use acvm::FieldElement; + use self::{brillig_block::BrilligBlock, brillig_fn::FunctionContext}; use super::brillig_ir::{artifact::BrilligArtifact, BrilligContext}; use crate::ssa::ir::function::Function; /// Converting an SSA function into Brillig bytecode. -pub(crate) fn convert_ssa_function(func: &Function, enable_debug_trace: bool) -> BrilligArtifact { +pub(crate) fn convert_ssa_function( + func: &Function, + enable_debug_trace: bool, +) -> BrilligArtifact { let mut brillig_context = BrilligContext::new(enable_debug_trace); let mut function_context = FunctionContext::new(func); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index f56c5daf315..367cdbe4973 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -1,18 +1,19 @@ use acvm::{ acir::{brillig::BlackBoxOp, BlackBoxFunc}, - FieldElement, + AcirField, }; use crate::brillig::brillig_ir::{ brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, + debug_show::DebugToString, BrilligBinaryOp, BrilligContext, }; /// Transforms SSA's black box function calls into the corresponding brillig instructions /// Extracting arguments and results from the SSA function call /// And making any necessary type conversions to adapt noir's blackbox calls to brillig's -pub(crate) fn convert_black_box_call( - brillig_context: &mut BrilligContext, +pub(crate) fn convert_black_box_call( + brillig_context: &mut BrilligContext, bb_func: &BlackBoxFunc, function_arguments: &[BrilligVariable], function_results: &[BrilligVariable], @@ -341,7 +342,7 @@ pub(crate) fn convert_black_box_call( let inputs_vector = convert_array_or_vector(brillig_context, inputs, bb_func); let modulus_vector = convert_array_or_vector(brillig_context, modulus, bb_func); let output_id = brillig_context.get_new_bigint_id(); - brillig_context.const_instruction(*output, FieldElement::from(output_id as u128)); + brillig_context.const_instruction(*output, F::from(output_id as u128)); brillig_context.black_box_op_instruction(BlackBoxOp::BigIntFromLeBytes { inputs: inputs_vector.to_heap_vector(), modulus: modulus_vector.to_heap_vector(), @@ -426,8 +427,8 @@ pub(crate) fn convert_black_box_call( } } -fn convert_array_or_vector( - brillig_context: &mut BrilligContext, +fn convert_array_or_vector( + brillig_context: &mut BrilligContext, array_or_vector: &BrilligVariable, bb_func: &BlackBoxFunc, ) -> BrilligVector { @@ -442,8 +443,8 @@ fn convert_array_or_vector( } } -fn prepare_bigint_output( - brillig_context: &mut BrilligContext, +fn prepare_bigint_output( + brillig_context: &mut BrilligContext, lhs_modulus: &SingleAddrVariable, rhs_modulus: &SingleAddrVariable, output: &SingleAddrVariable, @@ -465,6 +466,6 @@ fn prepare_bigint_output( brillig_context.deallocate_register(condition); // Set output id let output_id = brillig_context.get_new_bigint_id(); - brillig_context.const_instruction(*output, FieldElement::from(output_id as u128)); + brillig_context.const_instruction(*output, F::from(output_id as u128)); brillig_context.mov_instruction(modulus_id.address, lhs_modulus.address); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index f165427233e..b441e8be3eb 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -33,7 +33,7 @@ pub(crate) struct BrilligBlock<'block> { /// The basic block that is being converted pub(crate) block_id: BasicBlockId, /// Context for creating brillig opcodes - pub(crate) brillig_context: &'block mut BrilligContext, + pub(crate) brillig_context: &'block mut BrilligContext, /// Tracks the available variable during the codegen of the block pub(crate) variables: BlockVariables, /// For each instruction, the set of values that are not used anymore after it. @@ -44,7 +44,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA Basic block into a sequence of Brillig opcodes pub(crate) fn compile( function_context: &'block mut FunctionContext, - brillig_context: &'block mut BrilligContext, + brillig_context: &'block mut BrilligContext, block_id: BasicBlockId, dfg: &DataFlowGraph, ) { @@ -944,7 +944,7 @@ impl<'block> BrilligBlock<'block> { } pub(crate) fn store_variable_in_array_with_ctx( - ctx: &mut BrilligContext, + ctx: &mut BrilligContext, destination_pointer: MemoryAddress, index_register: SingleAddrVariable, value_variable: BrilligVariable, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index fb9a8577d94..ffbca256d28 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -1,3 +1,4 @@ +use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ @@ -50,7 +51,7 @@ impl BlockVariables { pub(crate) fn define_variable( &mut self, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { @@ -70,7 +71,7 @@ impl BlockVariables { pub(crate) fn define_single_addr_variable( &mut self, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value: ValueId, dfg: &DataFlowGraph, ) -> SingleAddrVariable { @@ -83,7 +84,7 @@ impl BlockVariables { &mut self, value_id: &ValueId, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, ) { assert!(self.available_variables.remove(value_id), "ICE: Variable is not available"); let variable = function_context @@ -122,7 +123,7 @@ impl BlockVariables { /// We keep constants block-local. pub(crate) fn allocate_constant( &mut self, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { @@ -154,9 +155,9 @@ pub(crate) fn compute_array_length(item_typ: &CompositeType, elem_count: usize) } /// For a given value_id, allocates the necessary registers to hold it. -pub(crate) fn allocate_value( +pub(crate) fn allocate_value( value_id: ValueId, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, dfg: &DataFlowGraph, ) -> BrilligVariable { let typ = dfg.type_of_value(value_id); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index 74319595795..ae159f2c45c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -1,13 +1,12 @@ use acvm::{ acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode}, acir::AcirField, - FieldElement, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; /// Generates brillig bytecode which computes the inverse of its input if not null, and zero else. -pub(crate) fn directive_invert() -> GeneratedBrillig { +pub(crate) fn directive_invert() -> GeneratedBrillig { // We generate the following code: // fn invert(x : Field) -> Field { // 1/ x @@ -28,8 +27,8 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // Put value zero in register (2) BrilligOpcode::Const { destination: zero_const, - value: FieldElement::from(0_usize), - bit_size: FieldElement::max_num_bits(), + value: F::from(0_usize), + bit_size: F::max_num_bits(), }, BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, @@ -42,8 +41,8 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // Put value one in register (1) BrilligOpcode::Const { destination: one_const, - value: FieldElement::from(1_usize), - bit_size: FieldElement::max_num_bits(), + value: F::one(), + bit_size: F::max_num_bits(), }, // Divide 1 by the input, and set the result of the division into register (0) BrilligOpcode::BinaryFieldOp { @@ -68,13 +67,13 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { /// (a/b, a-a/b*b) /// } /// ``` -pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { +pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { // `a` is (0) (i.e register index 0) // `b` is (1) // TODO: The only difference between these implementations is the integer version will truncate the input to the `bit_size` via cast. // Once we deduplicate brillig functions then we can modify this so that fields and integers share the same quotient function. - if bit_size >= FieldElement::max_num_bits() { + if bit_size >= F::max_num_bits() { // Field version GeneratedBrillig { byte_code: vec![ diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index 000d1230ece..f0752c80c46 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -28,7 +28,7 @@ pub(crate) struct FunctionContext { } impl FunctionContext { - /// Creates a new function context. It will compute the liveness of every variable. + /// Creates a new function context. It will allocate parameters for all blocks and compute the liveness of every variable. pub(crate) fn new(function: &Function) -> Self { let id = function.id(); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 491086e8c0f..d17b15a13b5 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -372,7 +372,7 @@ mod tests { use crate::ssa::ir::map::Id; use crate::ssa::ssa_gen::Ssa; - fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) { + fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) { let mut builder = FunctionBuilder::new("main".to_string(), Id::test_new(0)); builder.set_runtime(RuntimeType::Brillig); @@ -385,7 +385,7 @@ mod tests { fn create_brillig_block<'a>( function_context: &'a mut FunctionContext, - brillig_context: &'a mut BrilligContext, + brillig_context: &'a mut BrilligContext, ) -> BrilligBlock<'a> { let variables = BlockVariables::default(); BrilligBlock { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index ebccf7a0bf1..9785e073be9 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -25,11 +25,13 @@ mod instructions; pub(crate) use instructions::BrilligBinaryOp; -use self::{artifact::BrilligArtifact, registers::BrilligRegistersContext}; +use self::{ + artifact::BrilligArtifact, debug_show::DebugToString, registers::BrilligRegistersContext, +}; use crate::ssa::ir::dfg::CallStack; use acvm::{ acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, - FieldElement, + AcirField, }; use debug_show::DebugShow; @@ -76,8 +78,8 @@ impl ReservedRegisters { /// Brillig context object that is used while constructing the /// Brillig bytecode. -pub(crate) struct BrilligContext { - obj: BrilligArtifact, +pub(crate) struct BrilligContext { + obj: BrilligArtifact, /// Tracks register allocations registers: BrilligRegistersContext, /// Context label, must be unique with respect to the function @@ -93,9 +95,9 @@ pub(crate) struct BrilligContext { bigint_new_id: u32, } -impl BrilligContext { +impl BrilligContext { /// Initial context state - pub(crate) fn new(enable_debug_trace: bool) -> BrilligContext { + pub(crate) fn new(enable_debug_trace: bool) -> BrilligContext { BrilligContext { obj: BrilligArtifact::default(), registers: BrilligRegistersContext::new(), @@ -113,12 +115,12 @@ impl BrilligContext { result } /// Adds a brillig instruction to the brillig byte code - fn push_opcode(&mut self, opcode: BrilligOpcode) { + fn push_opcode(&mut self, opcode: BrilligOpcode) { self.obj.push_opcode(opcode); } /// Returns the artifact - pub(crate) fn artifact(self) -> BrilligArtifact { + pub(crate) fn artifact(self) -> BrilligArtifact { self.obj } @@ -200,17 +202,17 @@ pub(crate) mod tests { } } - pub(crate) fn create_context() -> BrilligContext { + pub(crate) fn create_context() -> BrilligContext { let mut context = BrilligContext::new(true); context.enter_context("test"); context } pub(crate) fn create_entry_point_bytecode( - context: BrilligContext, + context: BrilligContext, arguments: Vec, returns: Vec, - ) -> GeneratedBrillig { + ) -> GeneratedBrillig { let artifact = context.artifact(); let mut entry_point_artifact = BrilligContext::new_entry_point_artifact(arguments, returns, "test".to_string()); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 99e922c1580..2d0bdb5955c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -1,4 +1,4 @@ -use acvm::{acir::brillig::Opcode as BrilligOpcode, FieldElement}; +use acvm::acir::brillig::Opcode as BrilligOpcode; use std::collections::{BTreeMap, HashMap}; use crate::ssa::ir::dfg::CallStack; @@ -18,8 +18,8 @@ pub(crate) enum BrilligParameter { /// The result of compiling and linking brillig artifacts. /// This is ready to run bytecode with attached metadata. #[derive(Debug, Default)] -pub(crate) struct GeneratedBrillig { - pub(crate) byte_code: Vec>, +pub(crate) struct GeneratedBrillig { + pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, } @@ -27,8 +27,8 @@ pub(crate) struct GeneratedBrillig { #[derive(Default, Debug, Clone)] /// Artifacts resulting from the compilation of a function into brillig byte code. /// It includes the bytecode of the function and all the metadata that allows linking with other functions. -pub(crate) struct BrilligArtifact { - pub(crate) byte_code: Vec>, +pub(crate) struct BrilligArtifact { + pub(crate) byte_code: Vec>, /// A map of bytecode positions to assertion messages. /// Some error messages (compiler intrinsics) are not emitted via revert data, /// instead, they are handled externally so they don't add size to user programs. @@ -73,9 +73,9 @@ pub(crate) type JumpInstructionPosition = OpcodeLocation; /// to their position in the bytecode. pub(crate) type UnresolvedJumpLocation = Label; -impl BrilligArtifact { +impl BrilligArtifact { /// Resolves all jumps and generates the final bytecode - pub(crate) fn finish(mut self) -> GeneratedBrillig { + pub(crate) fn finish(mut self) -> GeneratedBrillig { self.resolve_jumps(); GeneratedBrillig { byte_code: self.byte_code, @@ -94,7 +94,7 @@ impl BrilligArtifact { /// This method will offset the positions in the Brillig artifact to /// account for the fact that it is being appended to the end of this /// Brillig artifact (self). - pub(crate) fn link_with(&mut self, obj: &BrilligArtifact) { + pub(crate) fn link_with(&mut self, obj: &BrilligArtifact) { // Add the unresolved jumps of the linked function to this artifact. self.add_unresolved_jumps_and_calls(obj); @@ -128,7 +128,7 @@ impl BrilligArtifact { } /// Adds unresolved jumps & function calls from another artifact offset by the current opcode count in the artifact. - fn add_unresolved_jumps_and_calls(&mut self, obj: &BrilligArtifact) { + fn add_unresolved_jumps_and_calls(&mut self, obj: &BrilligArtifact) { let offset = self.index_of_next_opcode(); for (jump_label, jump_location) in &obj.unresolved_jumps { self.unresolved_jumps.push((jump_label + offset, jump_location.clone())); @@ -154,7 +154,7 @@ impl BrilligArtifact { } /// Adds a brillig instruction to the brillig byte code - pub(crate) fn push_opcode(&mut self, opcode: BrilligOpcode) { + pub(crate) fn push_opcode(&mut self, opcode: BrilligOpcode) { if !self.call_stack.is_empty() { self.locations.insert(self.index_of_next_opcode(), self.call_stack.clone()); } @@ -164,7 +164,7 @@ impl BrilligArtifact { /// Adds a unresolved jump to be fixed at the end of bytecode processing. pub(crate) fn add_unresolved_jump( &mut self, - jmp_instruction: BrilligOpcode, + jmp_instruction: BrilligOpcode, destination: UnresolvedJumpLocation, ) { assert!( @@ -178,7 +178,7 @@ impl BrilligArtifact { /// Adds a unresolved external call that will be fixed once linking has been done. pub(crate) fn add_unresolved_external_call( &mut self, - call_instruction: BrilligOpcode, + call_instruction: BrilligOpcode, destination: UnresolvedJumpLocation, ) { // TODO: Add a check to ensure that the opcode is a call instruction @@ -188,7 +188,7 @@ impl BrilligArtifact { } /// Returns true if the opcode is a jump instruction - fn is_jmp_instruction(instruction: &BrilligOpcode) -> bool { + fn is_jmp_instruction(instruction: &BrilligOpcode) -> bool { matches!( instruction, BrilligOpcode::JumpIfNot { .. } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs index 4ef279bd532..a9c4f238491 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -1,8 +1,8 @@ -use acvm::{acir::brillig::MemoryAddress, FieldElement}; +use acvm::{acir::brillig::MemoryAddress, AcirField}; -use super::{instructions::BrilligBinaryOp, BrilligContext}; +use super::{debug_show::DebugToString, instructions::BrilligBinaryOp, BrilligContext}; -impl BrilligContext { +impl BrilligContext { /// Utility method to perform a binary instruction with a constant value in place pub(crate) fn codegen_usize_op_in_place( &mut self, @@ -21,7 +21,7 @@ impl BrilligContext { op: BrilligBinaryOp, constant: usize, ) { - let const_register = self.make_usize_constant_instruction(FieldElement::from(constant)); + let const_register = self.make_usize_constant_instruction(F::from(constant)); self.memory_op_instruction(operand, const_register.address, destination, op); // Mark as no longer used for this purpose, frees for reuse self.deallocate_single_addr(const_register); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs index db65849a6b8..2d93cf70181 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -1,10 +1,11 @@ -use acvm::acir::brillig::MemoryAddress; +use acvm::{acir::brillig::MemoryAddress, AcirField}; use super::{ - brillig_variable::BrilligVariable, BrilligBinaryOp, BrilligContext, ReservedRegisters, + brillig_variable::BrilligVariable, debug_show::DebugToString, BrilligBinaryOp, BrilligContext, + ReservedRegisters, }; -impl BrilligContext { +impl BrilligContext { /// Saves all of the registers that have been used up until this point. fn codegen_save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { // Save all of the used registers at this point in memory diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs index fee3a450119..10badcd7308 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -1,12 +1,16 @@ -use acvm::acir::brillig::{HeapArray, MemoryAddress}; +use acvm::{ + acir::brillig::{HeapArray, MemoryAddress}, + AcirField, +}; use super::{ artifact::BrilligParameter, brillig_variable::{BrilligVariable, SingleAddrVariable}, + debug_show::DebugToString, BrilligBinaryOp, BrilligContext, ReservedRegisters, }; -impl BrilligContext { +impl BrilligContext { /// Codegens a return from the current function. /// /// For Brillig, the return is implicit, since there is no explicit return instruction. @@ -36,10 +40,11 @@ impl BrilligContext { /// This codegen will issue a loop that will iterate iteration_count times /// The body of the loop should be issued by the caller in the on_iteration closure. - pub(crate) fn codegen_loop(&mut self, iteration_count: MemoryAddress, on_iteration: F) - where - F: FnOnce(&mut BrilligContext, SingleAddrVariable), - { + pub(crate) fn codegen_loop( + &mut self, + iteration_count: MemoryAddress, + on_iteration: impl FnOnce(&mut BrilligContext, SingleAddrVariable), + ) { let iterator_register = self.make_usize_constant_instruction(0_u128.into()); let (loop_section, loop_label) = self.reserve_next_section_label(); @@ -87,7 +92,7 @@ impl BrilligContext { pub(crate) fn codegen_branch( &mut self, condition: MemoryAddress, - mut f: impl FnMut(&mut BrilligContext, bool), + mut f: impl FnMut(&mut BrilligContext, bool), ) { // Reserve 3 sections let (then_section, then_label) = self.reserve_next_section_label(); @@ -112,7 +117,7 @@ impl BrilligContext { pub(crate) fn codegen_if( &mut self, condition: MemoryAddress, - f: impl FnOnce(&mut BrilligContext), + f: impl FnOnce(&mut BrilligContext), ) { let (end_section, end_label) = self.reserve_next_section_label(); let (then_section, then_label) = self.reserve_next_section_label(); @@ -130,7 +135,7 @@ impl BrilligContext { pub(crate) fn codegen_if_not( &mut self, condition: MemoryAddress, - f: impl FnOnce(&mut BrilligContext), + f: impl FnOnce(&mut BrilligContext), ) { let (end_section, end_label) = self.reserve_next_section_label(); @@ -156,7 +161,7 @@ impl BrilligContext { let revert_data = HeapArray { pointer: ctx.allocate_register(), // + 1 due to the revert data id being the first item returned - size: BrilligContext::flattened_tuple_size(&revert_data_types) + 1, + size: Self::flattened_tuple_size(&revert_data_types) + 1, }; ctx.codegen_allocate_fixed_length_array(revert_data.pointer, revert_data.size); @@ -169,7 +174,7 @@ impl BrilligContext { for (revert_variable, revert_param) in revert_data_items.into_iter().zip(revert_data_types.into_iter()) { - let flattened_size = BrilligContext::flattened_size(&revert_param); + let flattened_size = Self::flattened_size(&revert_param); match revert_param { BrilligParameter::SingleAddr(_) => { ctx.store_instruction( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs index 42f3b34aea0..b1cb2b19764 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -1,15 +1,15 @@ use acvm::{ acir::brillig::{BlackBoxOp, HeapArray}, acir::AcirField, - FieldElement, }; use super::{ brillig_variable::{BrilligVector, SingleAddrVariable}, + debug_show::DebugToString, BrilligContext, }; -impl BrilligContext { +impl BrilligContext { /// Codegens a truncation of a value to the given bit size pub(crate) fn codegen_truncate( &mut self, @@ -43,7 +43,7 @@ impl BrilligContext { big_endian: bool, limb_bit_size: u32, ) { - assert!(source_field.bit_size == FieldElement::max_num_bits()); + assert!(source_field.bit_size == F::max_num_bits()); self.usize_const_instruction(target_vector.size, limb_count.into()); self.usize_const_instruction(target_vector.rc, 1_usize.into()); @@ -55,12 +55,10 @@ impl BrilligContext { output: HeapArray { pointer: target_vector.pointer, size: limb_count }, }); - let limb_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - + let limb_field = SingleAddrVariable::new(self.allocate_register(), F::max_num_bits()); let limb_casted = SingleAddrVariable::new(self.allocate_register(), limb_bit_size); - if limb_bit_size != FieldElement::max_num_bits() { + if limb_bit_size != F::max_num_bits() { self.codegen_loop(target_vector.size, |ctx, iterator_register| { // Read the limb ctx.codegen_array_get(target_vector.pointer, iterator_register, limb_field.address); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs index 15761113f51..81c1c3847b1 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -1,13 +1,14 @@ -use acvm::acir::brillig::MemoryAddress; +use acvm::{acir::brillig::MemoryAddress, AcirField}; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, + debug_show::DebugToString, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; -impl BrilligContext { +impl BrilligContext { /// Allocates an array of size `size` and stores the pointer to the array /// in `pointer_register` pub(crate) fn codegen_allocate_fixed_length_array( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs index 1c30f0f848f..943b0b9d7a3 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs @@ -1,8 +1,8 @@ -use acvm::acir::brillig::MemoryAddress; +use acvm::{acir::brillig::MemoryAddress, AcirField}; -use super::BrilligContext; +use super::{debug_show::DebugToString, BrilligContext}; -impl BrilligContext { +impl BrilligContext { /// This function moves values from a set of registers to another set of registers. /// It first moves all sources to new allocated registers to avoid overwriting. pub(crate) fn codegen_mov_registers_to_registers( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index def91f82bfd..075a776572e 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -8,7 +8,7 @@ use acvm::{ }; /// Trait for converting values into debug-friendly strings. -trait DebugToString { +pub(crate) trait DebugToString { fn debug_to_string(&self) -> String; } @@ -169,7 +169,7 @@ impl DebugShow { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: FieldElement) { + pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: F) { debug_println!(self.enable_debug_trace, " CONST {} = {}", result, constant); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 9023183eb36..dc06c2fa0d7 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,21 +1,21 @@ use super::{ artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, - debug_show::DebugShow, + debug_show::{DebugShow, DebugToString}, registers::BrilligRegistersContext, BrilligBinaryOp, BrilligContext, ReservedRegisters, }; -use acvm::{acir::brillig::MemoryAddress, acir::AcirField, FieldElement}; +use acvm::{acir::brillig::MemoryAddress, acir::AcirField}; pub(crate) const MAX_STACK_SIZE: usize = 2048; -impl BrilligContext { +impl BrilligContext { /// Creates an entry point artifact that will jump to the function label provided. pub(crate) fn new_entry_point_artifact( arguments: Vec, return_parameters: Vec, target_function: T, - ) -> BrilligArtifact { + ) -> BrilligArtifact { let mut context = BrilligContext { obj: BrilligArtifact::default(), registers: BrilligRegistersContext::new(), @@ -42,8 +42,8 @@ impl BrilligContext { arguments: &[BrilligParameter], return_parameters: &[BrilligParameter], ) { - let calldata_size = BrilligContext::flattened_tuple_size(arguments); - let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + let calldata_size = Self::flattened_tuple_size(arguments); + let return_data_size = Self::flattened_tuple_size(return_parameters); // Set initial value of stack pointer: MAX_STACK_SIZE + calldata_size + return_data_size self.const_instruction( @@ -74,7 +74,7 @@ impl BrilligContext { let pointer_to_the_array_in_calldata = self.make_usize_constant_instruction(current_calldata_pointer.into()); let rc_register = self.make_usize_constant_instruction(1_usize.into()); - let flattened_size = BrilligContext::flattened_size(argument); + let flattened_size = Self::flattened_size(argument); let var = BrilligVariable::BrilligArray(BrilligArray { pointer: pointer_to_the_array_in_calldata.address, size: flattened_size, @@ -88,7 +88,7 @@ impl BrilligContext { let pointer_to_the_array_in_calldata = self.make_usize_constant_instruction(current_calldata_pointer.into()); - let flattened_size = BrilligContext::flattened_size(argument); + let flattened_size = Self::flattened_size(argument); let size_register = self.make_usize_constant_instruction(flattened_size.into()); let rc_register = self.make_usize_constant_instruction(1_usize.into()); @@ -121,7 +121,7 @@ impl BrilligContext { BrilligVariable::BrilligVector(vector), BrilligParameter::Slice(item_type, item_count), ) => { - let flattened_size = BrilligContext::flattened_size(argument); + let flattened_size = Self::flattened_size(argument); let deflattened_address = self.deflatten_array(item_type, flattened_size, vector.pointer); @@ -139,7 +139,7 @@ impl BrilligContext { } fn copy_and_cast_calldata(&mut self, arguments: &[BrilligParameter]) { - let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let calldata_size = Self::flattened_tuple_size(arguments); self.calldata_copy_instruction(MemoryAddress(MAX_STACK_SIZE), calldata_size, 0); fn flat_bit_sizes(param: &BrilligParameter) -> Box + '_> { @@ -154,7 +154,7 @@ impl BrilligContext { for (i, bit_size) in arguments.iter().flat_map(flat_bit_sizes).enumerate() { // Calldatacopy tags everything with field type, so when downcast when necessary - if bit_size < FieldElement::max_num_bits() { + if bit_size < F::max_num_bits() { self.cast_instruction( SingleAddrVariable::new(MemoryAddress(MAX_STACK_SIZE + i), bit_size), SingleAddrVariable::new_field(MemoryAddress(MAX_STACK_SIZE + i)), @@ -169,7 +169,7 @@ impl BrilligContext { BrilligParameter::SingleAddr(_) => 1, BrilligParameter::Array(item_types, item_count) | BrilligParameter::Slice(item_types, item_count) => { - let item_size: usize = item_types.iter().map(BrilligContext::flattened_size).sum(); + let item_size: usize = item_types.iter().map(Self::flattened_size).sum(); item_count * item_size } } @@ -177,7 +177,7 @@ impl BrilligContext { /// Computes the size of a parameter if it was flattened pub(super) fn flattened_tuple_size(tuple: &[BrilligParameter]) -> usize { - tuple.iter().map(BrilligContext::flattened_size).sum() + tuple.iter().map(Self::flattened_size).sum() } /// Computes the size of a parameter if it was flattened @@ -193,12 +193,12 @@ impl BrilligContext { item_count: usize, flattened_array_pointer: MemoryAddress, ) -> MemoryAddress { - if BrilligContext::has_nested_arrays(item_type) { + if Self::has_nested_arrays(item_type) { let movement_register = self.allocate_register(); let deflattened_array_pointer = self.allocate_register(); let target_item_size = item_type.len(); - let source_item_size = BrilligContext::flattened_tuple_size(item_type); + let source_item_size = Self::flattened_tuple_size(item_type); self.codegen_allocate_fixed_length_array( deflattened_array_pointer, @@ -276,7 +276,7 @@ impl BrilligContext { .into_iter() .for_each(|register| self.deallocate_register(register)); - source_offset += BrilligContext::flattened_size(subitem); + source_offset += Self::flattened_size(subitem); } BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), } @@ -328,8 +328,8 @@ impl BrilligContext { .collect(); // Now, we deflatten the return data - let calldata_size = BrilligContext::flattened_tuple_size(arguments); - let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + let calldata_size = Self::flattened_tuple_size(arguments); + let return_data_size = Self::flattened_tuple_size(return_parameters); // Return data has a reserved space after calldata let return_data_offset = MAX_STACK_SIZE + calldata_size; @@ -357,7 +357,7 @@ impl BrilligContext { ); self.deallocate_single_addr(pointer_to_return_data); - return_data_index += BrilligContext::flattened_size(return_param); + return_data_index += Self::flattened_size(return_param); } BrilligParameter::Slice(..) => { unreachable!("ICE: Cannot return slices from brillig entrypoints") @@ -376,12 +376,11 @@ impl BrilligContext { flattened_array_pointer: MemoryAddress, deflattened_array_pointer: MemoryAddress, ) { - if BrilligContext::has_nested_arrays(item_type) { + if Self::has_nested_arrays(item_type) { let movement_register = self.allocate_register(); let source_item_size = item_type.len(); - let target_item_size: usize = - item_type.iter().map(BrilligContext::flattened_size).sum(); + let target_item_size: usize = item_type.iter().map(Self::flattened_size).sum(); for item_index in 0..item_count { let source_item_base_index = item_index * source_item_size; @@ -462,7 +461,7 @@ impl BrilligContext { .into_iter() .for_each(|register| self.deallocate_register(register)); - target_offset += BrilligContext::flattened_size(subitem); + target_offset += Self::flattened_size(subitem); } BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 03a9216b73a..a614f93fa30 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -10,12 +10,13 @@ use acvm::{ use super::{ artifact::UnresolvedJumpLocation, brillig_variable::{BrilligArray, BrilligVector, SingleAddrVariable}, + debug_show::DebugToString, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; /// Low level instructions of the brillig IR, used by the brillig ir codegens and brillig_gen /// Printed using debug_slow -impl BrilligContext { +impl BrilligContext { /// Processes a binary instruction according `operation`. /// /// This method will compute lhs rhs @@ -42,8 +43,7 @@ impl BrilligContext { ) { self.debug_show.not_instruction(input.address, input.bit_size, result.address); // Compile !x as ((-1) - x) - let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) - - FieldElement::one(); + let u_max = F::from(2_u128).pow(&F::from(input.bit_size as u128)) - F::one(); let max = self.make_constant(u_max, input.bit_size); self.binary(max, input, result, BrilligBinaryOp::Sub); @@ -63,7 +63,7 @@ impl BrilligContext { SingleAddrVariable::new_usize(rhs), SingleAddrVariable::new( destination, - BrilligContext::binary_result_bit_size(op, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), + Self::binary_result_bit_size(op, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), ), op, ); @@ -77,8 +77,7 @@ impl BrilligContext { operation: BrilligBinaryOp, ) { let is_field_op = lhs.bit_size == FieldElement::max_num_bits(); - let expected_result_bit_size = - BrilligContext::binary_result_bit_size(operation, lhs.bit_size); + let expected_result_bit_size = Self::binary_result_bit_size(operation, lhs.bit_size); assert!( result.bit_size == expected_result_bit_size, "Expected result bit size to be {}, got {} for operation {:?}", @@ -213,7 +212,7 @@ impl BrilligContext { /// Adds a unresolved `Jump` to the bytecode. fn add_unresolved_jump( &mut self, - jmp_instruction: BrilligOpcode, + jmp_instruction: BrilligOpcode, destination: UnresolvedJumpLocation, ) { self.obj.add_unresolved_jump(jmp_instruction, destination); @@ -369,12 +368,12 @@ impl BrilligContext { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: FieldElement) { + pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: F) { self.debug_show.const_instruction(result.address, constant); self.constant(result, constant); } - fn constant(&mut self, result: SingleAddrVariable, constant: FieldElement) { + fn constant(&mut self, result: SingleAddrVariable, constant: F) { assert!( result.bit_size >= constant.num_bits(), "Constant {} does not fit in bit size {}", @@ -382,10 +381,10 @@ impl BrilligContext { result.bit_size ); if result.bit_size > 128 && constant.num_bits() > 128 { - let high = FieldElement::from_be_bytes_reduce( + let high = F::from_be_bytes_reduce( constant.to_be_bytes().get(0..16).expect("FieldElement::to_be_bytes() too short!"), ); - let low = FieldElement::from(constant.to_u128()); + let low = F::from(constant.to_u128()); let high_register = SingleAddrVariable::new(self.allocate_register(), 254); let low_register = SingleAddrVariable::new(self.allocate_register(), 254); let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254); @@ -393,7 +392,7 @@ impl BrilligContext { self.constant(low_register, low); // I want to multiply high by 2^128, but I can't get that big constant in. // So I'll multiply by 2^64 twice. - self.constant(intermediate_register, FieldElement::from(1_u128 << 64)); + self.constant(intermediate_register, F::from(1_u128 << 64)); self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); // Now we can add. @@ -411,18 +410,14 @@ impl BrilligContext { } } - pub(crate) fn usize_const_instruction( - &mut self, - result: MemoryAddress, - constant: FieldElement, - ) { + pub(crate) fn usize_const_instruction(&mut self, result: MemoryAddress, constant: F) { self.const_instruction(SingleAddrVariable::new_usize(result), constant); } /// Returns a register which holds the value of a constant pub(crate) fn make_constant_instruction( &mut self, - constant: FieldElement, + constant: F, bit_size: u32, ) -> SingleAddrVariable { let var = SingleAddrVariable::new(self.allocate_register(), bit_size); @@ -430,17 +425,14 @@ impl BrilligContext { var } - fn make_constant(&mut self, constant: FieldElement, bit_size: u32) -> SingleAddrVariable { + fn make_constant(&mut self, constant: F, bit_size: u32) -> SingleAddrVariable { let var = SingleAddrVariable::new(self.allocate_register(), bit_size); self.constant(var, constant); var } /// Returns a register which holds the value of an usize constant - pub(crate) fn make_usize_constant_instruction( - &mut self, - constant: FieldElement, - ) -> SingleAddrVariable { + pub(crate) fn make_usize_constant_instruction(&mut self, constant: F) -> SingleAddrVariable { let register = self.allocate_register(); self.usize_const_instruction(register, constant); SingleAddrVariable::new_usize(register) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index f756f06aa69..ae506462b25 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -82,7 +82,7 @@ impl BrilligRegistersContext { } } -impl BrilligContext { +impl BrilligContext { /// Returns the i'th register after the reserved ones pub(crate) fn register(&self, i: usize) -> MemoryAddress { MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) diff --git a/compiler/noirc_evaluator/src/brillig/mod.rs b/compiler/noirc_evaluator/src/brillig/mod.rs index eda1ac97c1e..70c0959517b 100644 --- a/compiler/noirc_evaluator/src/brillig/mod.rs +++ b/compiler/noirc_evaluator/src/brillig/mod.rs @@ -1,6 +1,8 @@ pub(crate) mod brillig_gen; pub(crate) mod brillig_ir; +use acvm::FieldElement; + use self::{ brillig_gen::{brillig_fn::FunctionContext, convert_ssa_function}, brillig_ir::artifact::{BrilligArtifact, Label}, @@ -16,7 +18,7 @@ use std::collections::{BTreeSet, HashMap}; #[derive(Default)] pub struct Brillig { /// Maps SSA function labels to their brillig artifact - ssa_function_to_brillig: HashMap, + ssa_function_to_brillig: HashMap>, } impl Brillig { @@ -27,7 +29,10 @@ impl Brillig { } /// Finds a brillig function artifact by its function label - pub(crate) fn find_by_function_label(&self, function_label: Label) -> Option<&BrilligArtifact> { + pub(crate) fn find_by_function_label( + &self, + function_label: Label, + ) -> Option<&BrilligArtifact> { self.ssa_function_to_brillig.iter().find_map(|(function_id, obj)| { if FunctionContext::function_id_to_function_label(*function_id) == function_label { Some(obj) @@ -39,7 +44,7 @@ impl Brillig { } impl std::ops::Index for Brillig { - type Output = BrilligArtifact; + type Output = BrilligArtifact; fn index(&self, id: FunctionId) -> &Self::Output { &self.ssa_function_to_brillig[&id] } diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 80a63f223e7..f7b6dfd3771 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -206,7 +206,7 @@ pub struct SsaCircuitArtifact { } fn convert_generated_acir_into_circuit( - mut generated_acir: GeneratedAcir, + mut generated_acir: GeneratedAcir, func_sig: FunctionSignature, recursive: bool, debug_variables: DebugVariables, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index ac7f2c09687..56b869fbf6b 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -20,7 +20,6 @@ use acvm::{ native_types::{Expression, Witness}, BlackBoxFunc, }, - FieldElement, }; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; @@ -48,12 +47,12 @@ impl AcirType { } /// Returns the bit size of the underlying type - pub(crate) fn bit_size(&self) -> u32 { + pub(crate) fn bit_size(&self) -> u32 { match self { AcirType::NumericType(numeric_type) => match numeric_type { NumericType::Signed { bit_size } => *bit_size, NumericType::Unsigned { bit_size } => *bit_size, - NumericType::NativeField => FieldElement::max_num_bits(), + NumericType::NativeField => F::max_num_bits(), }, AcirType::Array(_, _) => unreachable!("cannot fetch bit size of array type"), } @@ -106,13 +105,13 @@ impl From for AcirType { /// Context object which holds the relationship between /// `Variables`(AcirVar) and types such as `Expression` and `Witness` /// which are placed into ACIR. -pub(crate) struct AcirContext { +pub(crate) struct AcirContext { /// Two-way map that links `AcirVar` to `AcirVarData`. /// /// The vars object is an instance of the `TwoWayMap`, which provides a bidirectional mapping between `AcirVar` and `AcirVarData`. - vars: HashMap, + vars: HashMap>, - constant_witnesses: HashMap, + constant_witnesses: HashMap, /// An in-memory representation of ACIR. /// @@ -121,13 +120,13 @@ pub(crate) struct AcirContext { /// For example, If one was to add two Variables together, /// then the `acir_ir` will be populated to assert this /// addition. - acir_ir: GeneratedAcir, + acir_ir: GeneratedAcir, /// The BigIntContext, used to generate identifiers for BigIntegers big_int_ctx: BigIntContext, } -impl AcirContext { +impl AcirContext { pub(crate) fn current_witness_index(&self) -> Witness { self.acir_ir.current_witness_index() } @@ -148,7 +147,7 @@ impl AcirContext { } /// Adds a constant to the context and assigns a Variable to represent it - pub(crate) fn add_constant(&mut self, constant: impl Into) -> AcirVar { + pub(crate) fn add_constant(&mut self, constant: impl Into) -> AcirVar { let constant_data = AcirVarData::Const(constant.into()); self.add_data(constant_data) } @@ -156,7 +155,7 @@ impl AcirContext { /// Returns the constant represented by the given variable. /// /// Panics: if the variable does not represent a constant. - pub(crate) fn constant(&self, var: AcirVar) -> FieldElement { + pub(crate) fn constant(&self, var: AcirVar) -> &F { self.vars[&var].as_constant().expect("ICE - expected the variable to be a constant value") } @@ -270,10 +269,7 @@ impl AcirContext { } /// Converts an [`AcirVar`] to an [`Expression`] - pub(crate) fn var_to_expression( - &self, - var: AcirVar, - ) -> Result, InternalError> { + pub(crate) fn var_to_expression(&self, var: AcirVar) -> Result, InternalError> { let var_data = match self.vars.get(&var) { Some(var_data) => var_data, None => { @@ -408,7 +404,7 @@ impl AcirContext { if lhs_expr == rhs_expr { // x ^ x == 0 - let zero = self.add_constant(FieldElement::zero()); + let zero = self.add_constant(F::zero()); return Ok(zero); } else if lhs_expr.is_zero() { // 0 ^ x == x @@ -418,14 +414,14 @@ impl AcirContext { return Ok(lhs); } - let bit_size = typ.bit_size(); + let bit_size = typ.bit_size::(); if bit_size == 1 { // Operands are booleans. // // a ^ b == a + b - 2*a*b let prod = self.mul_var(lhs, rhs)?; let sum = self.add_var(lhs, rhs)?; - self.add_mul_var(sum, -FieldElement::from(2_i128), prod) + self.add_mul_var(sum, -F::from(2_u128), prod) } else { let inputs = vec![AcirValue::Var(lhs, typ.clone()), AcirValue::Var(rhs, typ)]; let outputs = self.black_box_function(BlackBoxFunc::XOR, inputs, 1)?; @@ -448,11 +444,11 @@ impl AcirContext { return Ok(lhs); } else if lhs_expr.is_zero() || rhs_expr.is_zero() { // x & 0 == 0 and 0 & x == 0 - let zero = self.add_constant(FieldElement::zero()); + let zero = self.add_constant(F::zero()); return Ok(zero); } - let bit_size = typ.bit_size(); + let bit_size = typ.bit_size::(); if bit_size == 1 { // Operands are booleans. self.mul_var(lhs, rhs) @@ -480,7 +476,7 @@ impl AcirContext { return Ok(lhs); } - let bit_size = typ.bit_size(); + let bit_size = typ.bit_size::(); if bit_size == 1 { // Operands are booleans // a + b - ab @@ -502,7 +498,7 @@ impl AcirContext { &mut self, lhs: AcirVar, rhs: AcirVar, - assert_message: Option>, + assert_message: Option>, ) -> Result<(), RuntimeError> { let lhs_expr = self.var_to_expression(lhs)?; let rhs_expr = self.var_to_expression(rhs)?; @@ -531,7 +527,7 @@ impl AcirContext { pub(crate) fn vars_to_expressions_or_memory( &self, values: &[AcirValue], - ) -> Result>, RuntimeError> { + ) -> Result>, RuntimeError> { let mut result = Vec::with_capacity(values.len()); for value in values { match value { @@ -597,7 +593,7 @@ impl AcirContext { (AcirVarData::Const(constant), _) | (_, AcirVarData::Const(constant)) if constant.is_zero() => { - self.add_constant(FieldElement::zero()) + self.add_constant(F::zero()) } (AcirVarData::Const(lhs_constant), AcirVarData::Const(rhs_constant)) => { @@ -615,7 +611,7 @@ impl AcirContext { } (AcirVarData::Witness(lhs_witness), AcirVarData::Witness(rhs_witness)) => { let mut expr = Expression::default(); - expr.push_multiplication_term(FieldElement::one(), lhs_witness, rhs_witness); + expr.push_multiplication_term(F::one(), lhs_witness, rhs_witness); self.add_data(AcirVarData::Expr(expr)) } (AcirVarData::Expr(expression), AcirVarData::Witness(witness)) @@ -681,12 +677,7 @@ impl AcirContext { /// Adds a new Variable to context whose value will /// be constrained to be the expression `lhs + k * rhs` - fn add_mul_var( - &mut self, - lhs: AcirVar, - k: FieldElement, - rhs: AcirVar, - ) -> Result { + fn add_mul_var(&mut self, lhs: AcirVar, k: F, rhs: AcirVar) -> Result { let k_var = self.add_constant(k); let intermediate = self.mul_var(k_var, rhs)?; @@ -695,7 +686,7 @@ impl AcirContext { /// Adds a new variable that is constrained to be the logical NOT of `x`. pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result { - let bit_size = typ.bit_size(); + let bit_size = typ.bit_size::(); // Subtracting from max flips the bits let max = self.add_constant((1_u128 << bit_size) - 1); self.sub_var(max, x) @@ -709,8 +700,8 @@ impl AcirContext { bit_size: u32, predicate: AcirVar, ) -> Result<(AcirVar, AcirVar), RuntimeError> { - let zero = self.add_constant(FieldElement::zero()); - let one = self.add_constant(FieldElement::one()); + let zero = self.add_constant(F::zero()); + let one = self.add_constant(F::one()); let lhs_expr = self.var_to_expression(lhs)?; let rhs_expr = self.var_to_expression(rhs)?; @@ -829,7 +820,7 @@ impl AcirContext { // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' let mut avoid_overflow = false; - if max_q_bits + max_rhs_bits >= FieldElement::max_num_bits() - 1 { + if max_q_bits + max_rhs_bits >= F::max_num_bits() - 1 { // q*b+r can overflow; we avoid this when b is constant if rhs_expr.is_const() { avoid_overflow = true; @@ -843,16 +834,16 @@ impl AcirContext { if avoid_overflow { // we compute q0 = p/rhs let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); - let q0_big = FieldElement::modulus() / &rhs_big; - let q0 = FieldElement::from_be_bytes_reduce(&q0_big.to_bytes_be()); + let q0_big = F::modulus() / &rhs_big; + let q0 = F::from_be_bytes_reduce(&q0_big.to_bytes_be()); let q0_var = self.add_constant(q0); // when q == q0, b*q+r can overflow so we need to bound r to avoid the overflow. let size_predicate = self.eq_var(q0_var, quotient_var)?; let predicate = self.mul_var(size_predicate, predicate)?; // Ensure that there is no overflow, under q == q0 predicate - let max_r_big = FieldElement::modulus() - q0_big * rhs_big; - let max_r = FieldElement::from_be_bytes_reduce(&max_r_big.to_bytes_be()); + let max_r_big = F::modulus() - q0_big * rhs_big; + let max_r = F::from_be_bytes_reduce(&max_r_big.to_bytes_be()); let max_r_var = self.add_constant(max_r); let max_r_predicate = self.mul_var(predicate, max_r_var)?; @@ -897,7 +888,7 @@ impl AcirContext { } assert!( - bits < FieldElement::max_num_bits(), + bits < F::max_num_bits(), "range check with bit size of the prime field is not implemented yet" ); @@ -921,7 +912,7 @@ impl AcirContext { // however, since it is a constant, we can compute it's actual bit size let r_bit_size = bit_size_u128(r); // witness = lhs_offset + r - assert!(bits + r_bit_size < FieldElement::max_num_bits()); //we need to ensure lhs_offset + r does not overflow + assert!(bits + r_bit_size < F::max_num_bits()); //we need to ensure lhs_offset + r does not overflow let r_var = self.add_constant(r); let aor = self.add_var(lhs_offset, r_var)?; @@ -945,14 +936,13 @@ impl AcirContext { leading: AcirVar, max_bit_size: u32, ) -> Result { - let max_power_of_two = self.add_constant( - FieldElement::from(2_i128).pow(&FieldElement::from(max_bit_size as i128 - 1)), - ); + let max_power_of_two = + self.add_constant(F::from(2_u128).pow(&F::from(max_bit_size as u128 - 1))); let intermediate = self.sub_var(max_power_of_two, lhs)?; let intermediate = self.mul_var(intermediate, leading)?; - self.add_mul_var(lhs, FieldElement::from(2_i128), intermediate) + self.add_mul_var(lhs, F::from(2_u128), intermediate) } /// Returns the quotient and remainder such that lhs = rhs * quotient + remainder @@ -976,11 +966,10 @@ impl AcirContext { assert_ne!(bit_size, 0, "signed integer should have at least one bit"); // 2^{max_bit size-1} - let max_power_of_two = self.add_constant( - FieldElement::from(2_i128).pow(&FieldElement::from(bit_size as i128 - 1)), - ); - let zero = self.add_constant(FieldElement::zero()); - let one = self.add_constant(FieldElement::one()); + let max_power_of_two = + self.add_constant(F::from(2_u128).pow(&F::from(bit_size as u128 - 1))); + let zero = self.add_constant(F::zero()); + let one = self.add_constant(F::one()); // Get the sign bit of rhs by computing rhs / max_power_of_two let (rhs_leading, _) = self.euclidean_division_var(rhs, max_power_of_two, bit_size, one)?; @@ -1071,9 +1060,8 @@ impl AcirContext { max_bit_size: u32, ) -> Result { // 2^{rhs} - let divisor = - self.add_constant(FieldElement::from(2_u128).pow(&FieldElement::from(rhs as u128))); - let one = self.add_constant(FieldElement::one()); + let divisor = self.add_constant(F::from(2_u128).pow(&F::from(rhs as u128))); + let one = self.add_constant(F::one()); // Computes lhs = 2^{rhs} * q + r let (_, remainder) = self.euclidean_division_var(lhs, divisor, max_bit_size, one)?; @@ -1093,8 +1081,8 @@ impl AcirContext { rhs: AcirVar, bit_count: u32, ) -> Result { - let pow_last = self.add_constant(FieldElement::from(1_u128 << (bit_count - 1))); - let pow = self.add_constant(FieldElement::from(1_u128 << (bit_count))); + let pow_last = self.add_constant(F::from(1_u128 << (bit_count - 1))); + let pow = self.add_constant(F::from(1_u128 << (bit_count))); // We check whether the inputs have same sign or not by computing the XOR of their bit sign @@ -1163,10 +1151,9 @@ impl AcirContext { // Ensure that 2^{max_bits + 1} is less than the field size // // TODO: perhaps this should be a user error, instead of an assert - assert!(max_bits + 1 < FieldElement::max_num_bits()); + assert!(max_bits + 1 < F::max_num_bits()); - let two_max_bits = self - .add_constant(FieldElement::from(2_i128).pow(&FieldElement::from(max_bits as i128))); + let two_max_bits = self.add_constant(F::from(2_u128).pow(&F::from(max_bits as u128))); let diff = self.sub_var(lhs, rhs)?; let comparison_evaluation = self.add_var(diff, two_max_bits)?; @@ -1214,7 +1201,7 @@ impl AcirContext { // compute less than. let comparison = self.more_than_eq_var(lhs, rhs, bit_size)?; - let one = self.add_constant(FieldElement::one()); + let one = self.add_constant(F::one()); self.sub_var(one, comparison) // comparison_negated } @@ -1251,7 +1238,7 @@ impl AcirContext { } }; - (vec![domain_constant], Vec::new()) + (vec![*domain_constant], Vec::new()) } BlackBoxFunc::Poseidon2Permutation => { // The last argument is the state length, which must be a constant @@ -1276,7 +1263,7 @@ impl AcirContext { } }; - (vec![state_len], Vec::new()) + (vec![*state_len], Vec::new()) } BlackBoxFunc::BigIntAdd | BlackBoxFunc::BigIntSub @@ -1297,7 +1284,7 @@ impl AcirContext { output_count = 0; let mut field_inputs = Vec::new(); for i in const_inputs { - field_inputs.push(i?); + field_inputs.push(*i?); } if field_inputs[1] != field_inputs[3] { return Err(RuntimeError::BigIntModulus { call_stack: self.get_call_stack() }); @@ -1306,7 +1293,7 @@ impl AcirContext { let result_id = self.big_int_ctx.new_big_int(field_inputs[1]); ( vec![field_inputs[0], field_inputs[2]], - vec![result_id.bigint_id(), result_id.modulus_id()], + vec![result_id.bigint_id::(), result_id.modulus_id::()], ) } BlackBoxFunc::BigIntToLeBytes => { @@ -1323,10 +1310,10 @@ impl AcirContext { inputs = Vec::new(); let mut field_inputs = Vec::new(); for i in const_inputs { - field_inputs.push(i?); + field_inputs.push(*i?); } let bigint = self.big_int_ctx.get(field_inputs[0]); - let modulus = self.big_int_ctx.modulus(bigint.modulus_id()); + let modulus = self.big_int_ctx.modulus(bigint.modulus_id::()); let bytes_len = ((modulus - BigUint::from(1_u32)).bits() - 1) / 8 + 1; output_count = bytes_len as usize; assert!(bytes_len == 32); @@ -1339,7 +1326,7 @@ impl AcirContext { match inputs.pop().expect(invalid_input) { AcirValue::Array(values) => { for value in values { - modulus.push(self.vars[&value.into_var()?].as_constant().ok_or( + modulus.push(*self.vars[&value.into_var()?].as_constant().ok_or( RuntimeError::InternalError(InternalError::NotAConstant { name: "big integer".to_string(), call_stack: self.get_call_stack(), @@ -1359,9 +1346,8 @@ impl AcirContext { output_count = 0; let modulus_id = self.big_int_ctx.get_or_insert_modulus(big_modulus); - let result_id = - self.big_int_ctx.new_big_int(FieldElement::from(modulus_id as u128)); - (modulus, vec![result_id.bigint_id(), result_id.modulus_id()]) + let result_id = self.big_int_ctx.new_big_int(F::from(modulus_id as u128)); + (modulus, vec![result_id.bigint_id::(), result_id.modulus_id::()]) } BlackBoxFunc::AES128Encrypt => { let invalid_input = "aes128_encrypt - operation requires a plaintext to encrypt"; @@ -1376,7 +1362,7 @@ impl AcirContext { } }?; output_count = input_size + (16 - input_size % 16); - (vec![], vec![FieldElement::from(output_count as u128)]) + (vec![], vec![F::from(output_count as u128)]) } _ => (vec![], vec![]), }; @@ -1420,7 +1406,7 @@ impl AcirContext { // constants too. let witness_var = self.get_or_create_witness_var(input)?; let witness = self.var_to_witness(witness_var)?; - let num_bits = typ.bit_size(); + let num_bits = typ.bit_size::(); single_val_witnesses.push(FunctionInput { witness, num_bits }); } witnesses.push(single_val_witnesses); @@ -1531,7 +1517,7 @@ impl AcirContext { inputs: Vec, return_values: Vec, warnings: Vec, - ) -> GeneratedAcir { + ) -> GeneratedAcir { self.acir_ir.input_witnesses = inputs; self.acir_ir.return_witnesses = return_values; self.acir_ir.warnings = warnings; @@ -1543,7 +1529,7 @@ impl AcirContext { /// Variable can be seen as an index into the context. /// We use a two-way map so that it is efficient to lookup /// either the key or the value. - fn add_data(&mut self, data: AcirVarData) -> AcirVar { + fn add_data(&mut self, data: AcirVarData) -> AcirVar { let id = AcirVar(self.vars.len()); self.vars.insert(id, data); id @@ -1553,7 +1539,7 @@ impl AcirContext { pub(crate) fn brillig_call( &mut self, predicate: AcirVar, - generated_brillig: &GeneratedBrillig, + generated_brillig: &GeneratedBrillig, inputs: Vec, outputs: Vec, attempt_execution: bool, @@ -1561,14 +1547,14 @@ impl AcirContext { brillig_function_index: u32, brillig_stdlib_func: Option, ) -> Result, RuntimeError> { - let brillig_inputs: Vec> = + let brillig_inputs: Vec> = try_vecmap(inputs, |i| -> Result<_, InternalError> { match i { AcirValue::Var(var, _) => { Ok(BrilligInputs::Single(self.var_to_expression(var)?)) } AcirValue::Array(vars) => { - let mut var_expressions: Vec> = Vec::new(); + let mut var_expressions: Vec> = Vec::new(); for var in vars { self.brillig_array_input(&mut var_expressions, var)?; } @@ -1619,8 +1605,8 @@ impl AcirContext { brillig_stdlib_func, ); - fn range_constraint_value( - context: &mut AcirContext, + fn range_constraint_value( + context: &mut AcirContext, value: &AcirValue, ) -> Result<(), RuntimeError> { match value { @@ -1655,7 +1641,7 @@ impl AcirContext { fn brillig_array_input( &mut self, - var_expressions: &mut Vec>, + var_expressions: &mut Vec>, input: AcirValue, ) -> Result<(), InternalError> { match input { @@ -1714,8 +1700,8 @@ impl AcirContext { fn execute_brillig( &mut self, - code: &[BrilligOpcode], - inputs: &[BrilligInputs], + code: &[BrilligOpcode], + inputs: &[BrilligInputs], outputs_types: &[AcirType], ) -> Option> { let mut memory = (execute_brillig(code, inputs)?).into_iter(); @@ -1740,7 +1726,7 @@ impl AcirContext { &mut self, element_types: &[AcirType], size: usize, - memory_iter: &mut impl Iterator>, + memory_iter: &mut impl Iterator>, ) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { @@ -1821,7 +1807,7 @@ impl AcirContext { ) -> Result<(), InternalError> { let initialized_values = match optional_value { None => { - let zero = self.add_constant(FieldElement::zero()); + let zero = self.add_constant(F::zero()); let zero_witness = self.var_to_witness(zero)?; vec![zero_witness; len] } @@ -1893,13 +1879,13 @@ impl AcirContext { /// Enum representing the possible values that a /// Variable can be given. #[derive(Debug, Eq, Clone)] -enum AcirVarData { +enum AcirVarData { Witness(Witness), - Expr(Expression), - Const(FieldElement), + Expr(Expression), + Const(F), } -impl PartialEq for AcirVarData { +impl PartialEq for AcirVarData { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Witness(l0), Self::Witness(r0)) => l0 == r0, @@ -1911,23 +1897,26 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl std::hash::Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } } -impl AcirVarData { +impl AcirVarData { /// Returns a FieldElement, if the underlying `AcirVarData` /// represents a constant. - pub(crate) fn as_constant(&self) -> Option { + pub(crate) fn as_constant(&self) -> Option<&F> { if let AcirVarData::Const(field) = self { - return Some(*field); + return Some(field); } None } +} + +impl AcirVarData { /// Converts all enum variants to an Expression. - pub(crate) fn to_expression(&self) -> Cow> { + pub(crate) fn to_expression(&self) -> Cow> { match self { AcirVarData::Witness(witness) => Cow::Owned(Expression::from(*witness)), AcirVarData::Expr(expr) => Cow::Borrowed(expr), @@ -1936,23 +1925,17 @@ impl AcirVarData { } } -impl From for AcirVarData { - fn from(constant: FieldElement) -> Self { - AcirVarData::Const(constant) - } -} - -impl From for AcirVarData { +impl From for AcirVarData { fn from(witness: Witness) -> Self { AcirVarData::Witness(witness) } } -impl From> for AcirVarData { - fn from(expr: Expression) -> Self { +impl From> for AcirVarData { + fn from(expr: Expression) -> Self { // Prefer simpler variants if possible. if let Some(constant) = expr.to_const() { - AcirVarData::from(*constant) + AcirVarData::Const(*constant) } else if let Some(witness) = expr.to_witness() { AcirVarData::from(witness) } else { @@ -1970,12 +1953,12 @@ pub(crate) struct AcirVar(usize); /// Returns the finished state of the Brillig VM if execution can complete. /// /// Returns `None` if complete execution of the Brillig bytecode is not possible. -fn execute_brillig( - code: &[BrilligOpcode], - inputs: &[BrilligInputs], -) -> Option>> { +fn execute_brillig( + code: &[BrilligOpcode], + inputs: &[BrilligInputs], +) -> Option>> { // Set input values - let mut calldata: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents a constant or array of constants. // Iterate over each input and push it into registers and/or memory. diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs index b9c596d80c7..30297b42ecf 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::acir::AcirField; use num_bigint::BigUint; /// Represents a bigint value in the form (id, modulus) where @@ -11,12 +11,12 @@ pub(crate) struct BigIntId { } impl BigIntId { - pub(crate) fn bigint_id(&self) -> FieldElement { - FieldElement::from(self.bigint_id as u128) + pub(crate) fn bigint_id>(&self) -> F { + F::from(self.bigint_id as u128) } - pub(crate) fn modulus_id(&self) -> FieldElement { - FieldElement::from(self.modulus_id as u128) + pub(crate) fn modulus_id>(&self) -> F { + F::from(self.modulus_id as u128) } } @@ -29,7 +29,7 @@ pub(crate) struct BigIntContext { impl BigIntContext { /// Creates a new BigIntId for the given modulus identifier and returns it. - pub(crate) fn new_big_int(&mut self, modulus_id: FieldElement) -> BigIntId { + pub(crate) fn new_big_int(&mut self, modulus_id: F) -> BigIntId { let id = self.big_integers.len() as u32; let result = BigIntId { bigint_id: id, modulus_id: modulus_id.to_u128() as u32 }; self.big_integers.push(result); @@ -37,12 +37,12 @@ impl BigIntContext { } /// Returns the modulus corresponding to the given modulus index - pub(crate) fn modulus(&self, idx: FieldElement) -> BigUint { + pub(crate) fn modulus(&self, idx: F) -> BigUint { self.modulus[idx.to_u128() as usize].clone() } /// Returns the BigIntId corresponding to the given identifier - pub(crate) fn get(&self, id: FieldElement) -> BigIntId { + pub(crate) fn get(&self, id: F) -> BigIntId { self.big_integers[id.to_u128() as usize] } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 9a09e7c06ee..9d271f7cd9c 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -19,7 +19,6 @@ use acvm::acir::{ use acvm::{ acir::AcirField, acir::{circuit::directives::Directive, native_types::Expression}, - FieldElement, }; use iter_extended::vecmap; use num_bigint::BigUint; @@ -32,7 +31,7 @@ pub(crate) const PLACEHOLDER_BRILLIG_INDEX: u32 = 0; #[derive(Debug, Default)] /// The output of the Acir-gen pass, which should only be produced for entry point Acir functions -pub(crate) struct GeneratedAcir { +pub(crate) struct GeneratedAcir { /// The next witness index that may be declared. /// If witness index is `None` then we have not yet created a witness /// and thus next witness index that be declared is zero. @@ -42,7 +41,7 @@ pub(crate) struct GeneratedAcir { current_witness_index: Option, /// The opcodes of which the compiled ACIR will comprise. - opcodes: Vec>, + opcodes: Vec>, /// All witness indices that comprise the final return value of the program pub(crate) return_witnesses: Vec, @@ -58,7 +57,7 @@ pub(crate) struct GeneratedAcir { pub(crate) call_stack: CallStack, /// Correspondence between an opcode index and the error message associated with it. - pub(crate) assertion_payloads: BTreeMap>, + pub(crate) assertion_payloads: BTreeMap>, pub(crate) warnings: Vec, @@ -80,7 +79,7 @@ pub(crate) enum BrilligStdlibFunc { } impl BrilligStdlibFunc { - pub(crate) fn get_generated_brillig(&self) -> GeneratedBrillig { + pub(crate) fn get_generated_brillig(&self) -> GeneratedBrillig { match self { BrilligStdlibFunc::Inverse => brillig_directive::directive_invert(), BrilligStdlibFunc::Quotient(bit_size) => { @@ -90,25 +89,25 @@ impl BrilligStdlibFunc { } } -impl GeneratedAcir { +impl GeneratedAcir { /// Returns the current witness index. pub(crate) fn current_witness_index(&self) -> Witness { Witness(self.current_witness_index.unwrap_or(0)) } /// Adds a new opcode into ACIR. - pub(crate) fn push_opcode(&mut self, opcode: AcirOpcode) { + pub(crate) fn push_opcode(&mut self, opcode: AcirOpcode) { self.opcodes.push(opcode); if !self.call_stack.is_empty() { self.locations.insert(self.last_acir_opcode_location(), self.call_stack.clone()); } } - pub(crate) fn opcodes(&self) -> &[AcirOpcode] { + pub(crate) fn opcodes(&self) -> &[AcirOpcode] { &self.opcodes } - pub(crate) fn take_opcodes(&mut self) -> Vec> { + pub(crate) fn take_opcodes(&mut self) -> Vec> { std::mem::take(&mut self.opcodes) } @@ -127,7 +126,7 @@ impl GeneratedAcir { /// /// If `expr` can be represented as a `Witness` then this function will return it, /// else a new opcode will be added to create a `Witness` that is equal to `expr`. - pub(crate) fn get_or_create_witness(&mut self, expr: &Expression) -> Witness { + pub(crate) fn get_or_create_witness(&mut self, expr: &Expression) -> Witness { match expr.to_witness() { Some(witness) => witness, None => self.create_witness_for_expression(expr), @@ -140,10 +139,7 @@ impl GeneratedAcir { /// This means you cannot multiply an infinite amount of `Expression`s together. /// Once the `Expression` goes over degree-2, then it needs to be reduced to a `Witness` /// which has degree-1 in order to be able to continue the multiplication chain. - pub(crate) fn create_witness_for_expression( - &mut self, - expression: &Expression, - ) -> Witness { + pub(crate) fn create_witness_for_expression(&mut self, expression: &Expression) -> Witness { let fresh_witness = self.next_witness_index(); // Create a constraint that sets them to be equal to each other @@ -163,15 +159,15 @@ impl GeneratedAcir { } } -impl GeneratedAcir { +impl GeneratedAcir { /// Calls a black box function and returns the output /// of said blackbox function. pub(crate) fn call_black_box( &mut self, func_name: BlackBoxFunc, inputs: &[Vec], - constant_inputs: Vec, - constant_outputs: Vec, + constant_inputs: Vec, + constant_outputs: Vec, output_count: usize, ) -> Result, InternalError> { let input_count = inputs.iter().fold(0usize, |sum, val| sum + val.len()); @@ -388,7 +384,7 @@ impl GeneratedAcir { /// Only radix that are a power of two are supported pub(crate) fn radix_le_decompose( &mut self, - input_expr: &Expression, + input_expr: &Expression, radix: u32, limb_count: u32, bit_size: u32, @@ -414,7 +410,7 @@ impl GeneratedAcir { self.range_constraint(*limb_witness, bit_size)?; composed_limbs = composed_limbs.add_mul( - FieldElement::from_be_bytes_reduce(&radix_pow.to_bytes_be()), + F::from_be_bytes_reduce(&radix_pow.to_bytes_be()), &Expression::from(*limb_witness), ); @@ -434,7 +430,7 @@ impl GeneratedAcir { /// /// Safety: It is the callers responsibility to ensure that the /// resulting `Witness` is constrained to be the inverse. - pub(crate) fn brillig_inverse(&mut self, expr: Expression) -> Witness { + pub(crate) fn brillig_inverse(&mut self, expr: Expression) -> Witness { // Create the witness for the result let inverted_witness = self.next_witness_index(); @@ -458,18 +454,14 @@ impl GeneratedAcir { /// /// If `expr` is not zero, then the constraint system will /// fail upon verification. - pub(crate) fn assert_is_zero(&mut self, expr: Expression) { + pub(crate) fn assert_is_zero(&mut self, expr: Expression) { self.push_opcode(AcirOpcode::AssertZero(expr)); } /// Returns a `Witness` that is constrained to be: /// - `1` if `lhs == rhs` /// - `0` otherwise - pub(crate) fn is_equal( - &mut self, - lhs: &Expression, - rhs: &Expression, - ) -> Witness { + pub(crate) fn is_equal(&mut self, lhs: &Expression, rhs: &Expression) -> Witness { let t = lhs - rhs; self.is_zero(&t) @@ -527,13 +519,13 @@ impl GeneratedAcir { /// By setting `z` to be `0`, we can make `y` equal to `1`. /// This is easily observed: `y = 1 - t * 0` /// Now since `y` is one, this means that `t` needs to be zero, or else `y * t == 0` will fail. - fn is_zero(&mut self, t_expr: &Expression) -> Witness { + fn is_zero(&mut self, t_expr: &Expression) -> Witness { // We're checking for equality with zero so we can negate the expression without changing the result. // This is useful as it will sometimes allow us to simplify an expression down to a witness. let t_witness = if let Some(witness) = t_expr.to_witness() { witness } else { - let negated_expr = t_expr * -FieldElement::one(); + let negated_expr = t_expr * -F::one(); self.get_or_create_witness(&negated_expr) }; @@ -545,17 +537,17 @@ impl GeneratedAcir { // Add constraint y == 1 - tz => y + tz - 1 == 0 let y_is_boolean_constraint = Expression { - mul_terms: vec![(FieldElement::one(), t_witness, z)], - linear_combinations: vec![(FieldElement::one(), y)], - q_c: -FieldElement::one(), + mul_terms: vec![(F::one(), t_witness, z)], + linear_combinations: vec![(F::one(), y)], + q_c: -F::one(), }; self.assert_is_zero(y_is_boolean_constraint); // Add constraint that y * t == 0; let ty_zero_constraint = Expression { - mul_terms: vec![(FieldElement::one(), t_witness, y)], + mul_terms: vec![(F::one(), t_witness, y)], linear_combinations: vec![], - q_c: FieldElement::zero(), + q_c: F::zero(), }; self.assert_is_zero(ty_zero_constraint); @@ -571,9 +563,9 @@ impl GeneratedAcir { ) -> Result<(), RuntimeError> { // We class this as an error because users should instead // do `as Field`. - if num_bits >= FieldElement::max_num_bits() { + if num_bits >= F::max_num_bits() { return Err(RuntimeError::InvalidRangeConstraint { - num_bits: FieldElement::max_num_bits(), + num_bits: F::max_num_bits(), call_stack: self.call_stack.clone(), }); }; @@ -588,9 +580,9 @@ impl GeneratedAcir { pub(crate) fn brillig_call( &mut self, - predicate: Option>, - generated_brillig: &GeneratedBrillig, - inputs: Vec>, + predicate: Option>, + generated_brillig: &GeneratedBrillig, + inputs: Vec>, outputs: Vec, brillig_function_index: u32, stdlib_func: Option, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 40170e58a30..aaa483b91c9 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -42,11 +42,11 @@ use im::Vector; use iter_extended::{try_vecmap, vecmap}; #[derive(Default)] -struct SharedContext { +struct SharedContext { /// Final list of Brillig functions which will be part of the final program /// This is shared across `Context` structs as we want one list of Brillig /// functions across all ACIR artifacts - generated_brillig: Vec, + generated_brillig: Vec>, /// Maps SSA function index -> Final generated Brillig artifact index. /// There can be Brillig functions specified in SSA which do not act as @@ -65,7 +65,7 @@ struct SharedContext { brillig_stdlib_calls_to_resolve: HashMap>, } -impl SharedContext { +impl SharedContext { fn generated_brillig_pointer( &self, func_id: FunctionId, @@ -74,7 +74,7 @@ impl SharedContext { self.brillig_generated_func_pointers.get(&(func_id, arguments)) } - fn generated_brillig(&self, func_pointer: usize) -> &GeneratedBrillig { + fn generated_brillig(&self, func_pointer: usize) -> &GeneratedBrillig { &self.generated_brillig[func_pointer] } @@ -83,7 +83,7 @@ impl SharedContext { func_id: FunctionId, arguments: Vec, generated_pointer: u32, - code: GeneratedBrillig, + code: GeneratedBrillig, ) { self.brillig_generated_func_pointers.insert((func_id, arguments), generated_pointer); self.generated_brillig.push(code); @@ -123,7 +123,7 @@ impl SharedContext { generated_pointer: u32, func_id: FunctionId, opcode_location: OpcodeLocation, - code: GeneratedBrillig, + code: GeneratedBrillig, ) { self.brillig_stdlib_func_pointer.insert(brillig_stdlib_func, generated_pointer); self.add_call_to_resolve(func_id, (opcode_location, generated_pointer)); @@ -151,7 +151,7 @@ struct Context<'a> { current_side_effects_enabled_var: AcirVar, /// Manages and builds the `AcirVar`s to which the converted SSA values refer. - acir_context: AcirContext, + acir_context: AcirContext, /// Track initialized acir dynamic arrays /// @@ -189,7 +189,7 @@ struct Context<'a> { data_bus: DataBus, /// Contains state that is generated and also used across ACIR functions - shared_context: &'a mut SharedContext, + shared_context: &'a mut SharedContext, } #[derive(Clone)] @@ -274,8 +274,11 @@ impl AcirValue { } } -pub(crate) type Artifacts = - (Vec, Vec>, BTreeMap); +pub(crate) type Artifacts = ( + Vec>, + Vec>, + BTreeMap, +); impl Ssa { #[tracing::instrument(level = "trace", skip_all)] @@ -331,7 +334,7 @@ impl Ssa { } impl<'a> Context<'a> { - fn new(shared_context: &'a mut SharedContext) -> Context<'a> { + fn new(shared_context: &'a mut SharedContext) -> Context<'a> { let mut acir_context = AcirContext::default(); let current_side_effects_enabled_var = acir_context.add_constant(FieldElement::one()); @@ -354,7 +357,7 @@ impl<'a> Context<'a> { ssa: &Ssa, function: &Function, brillig: &Brillig, - ) -> Result, RuntimeError> { + ) -> Result>, RuntimeError> { match function.runtime() { RuntimeType::Acir(inline_type) => { match inline_type { @@ -386,7 +389,7 @@ impl<'a> Context<'a> { main_func: &Function, ssa: &Ssa, brillig: &Brillig, - ) -> Result { + ) -> Result, RuntimeError> { let dfg = &main_func.dfg; let entry_block = &dfg[main_func.entry_block()]; let input_witness = self.convert_ssa_block_params(entry_block.parameters(), dfg)?; @@ -435,7 +438,7 @@ impl<'a> Context<'a> { mut self, main_func: &Function, brillig: &Brillig, - ) -> Result { + ) -> Result, RuntimeError> { let dfg = &main_func.dfg; let inputs = try_vecmap(dfg[main_func.entry_block()].parameters(), |param_id| { @@ -919,7 +922,7 @@ impl<'a> Context<'a> { func: &Function, arguments: Vec, brillig: &Brillig, - ) -> Result { + ) -> Result, InternalError> { // Create the entry point artifact let mut entry_point = BrilligContext::new_entry_point_artifact( arguments, @@ -1897,7 +1900,7 @@ impl<'a> Context<'a> { } let binary_type = AcirType::from(binary_type); - let bit_count = binary_type.bit_size(); + let bit_count = binary_type.bit_size::(); let num_type = binary_type.to_numeric_type(); let result = match binary.operator { BinaryOp::Add => self.acir_context.add_var(lhs, rhs), From 8bfaa93c9b6f0f615faa29bd2889eb96a8e9b85f Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:27:16 +0100 Subject: [PATCH 26/57] chore: add property tests for ABI encoding (#5216) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5226 ## Summary\* This PR adds property testing to ABI encoding so that we can show that any valid `Abi` + `InputMap` combination can be abi encoded and decoded to return the original inputs. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 25 +++-- Cargo.toml | 2 + cspell.json | 1 + tooling/noirc_abi/Cargo.toml | 5 +- tooling/noirc_abi/src/arbitrary.rs | 154 +++++++++++++++++++++++++++++ tooling/noirc_abi/src/lib.rs | 67 ++++--------- 6 files changed, 197 insertions(+), 57 deletions(-) create mode 100644 tooling/noirc_abi/src/arbitrary.rs diff --git a/Cargo.lock b/Cargo.lock index a8eab53dc07..7b96737f241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2730,6 +2730,8 @@ dependencies = [ "noirc_printable_type", "num-bigint", "num-traits", + "proptest", + "proptest-derive", "serde", "serde_json", "strum", @@ -3329,9 +3331,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", @@ -3341,12 +3343,23 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.7.4", + "regex-syntax 0.8.2", "rusty-fork", "tempfile", "unarray", ] +[[package]] +name = "proptest-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3578,12 +3591,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - [[package]] name = "regex-syntax" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index c08e9f9f38b..639807819ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,6 +141,8 @@ jsonrpc = { version = "0.16.0", features = ["minreq_http"] } flate2 = "1.0.24" rand = "0.8.5" proptest = "1.2.0" +proptest-derive = "0.4.0" + im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" diff --git a/cspell.json b/cspell.json index 9eb6e6f9239..2fb20ae2ba4 100644 --- a/cspell.json +++ b/cspell.json @@ -166,6 +166,7 @@ "rfind", "rustc", "rustup", + "sboxed", "schnorr", "sdiv", "secp256k1", diff --git a/tooling/noirc_abi/Cargo.toml b/tooling/noirc_abi/Cargo.toml index d450b268aee..e8f57060367 100644 --- a/tooling/noirc_abi/Cargo.toml +++ b/tooling/noirc_abi/Cargo.toml @@ -22,7 +22,10 @@ num-traits = "0.2" [dev-dependencies] strum = "0.24" strum_macros = "0.24" +proptest.workspace = true +proptest-derive.workspace = true + [features] bn254 = ["acvm/bn254"] -bls12_381 = ["acvm/bls12_381"] +bls12_381 = ["acvm/bls12_381"] \ No newline at end of file diff --git a/tooling/noirc_abi/src/arbitrary.rs b/tooling/noirc_abi/src/arbitrary.rs new file mode 100644 index 00000000000..aecb620b79d --- /dev/null +++ b/tooling/noirc_abi/src/arbitrary.rs @@ -0,0 +1,154 @@ +use iter_extended::{btree_map, vecmap}; +use prop::collection::vec; +use proptest::prelude::*; + +use acvm::{AcirField, FieldElement}; + +use crate::{ + input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, InputMap, + Sign, +}; +use std::collections::{BTreeMap, HashSet}; + +pub(super) use proptest_derive::Arbitrary; + +/// Mutates an iterator of mutable references to [`String`]s to ensure that all values are unique. +fn ensure_unique_strings<'a>(iter: impl Iterator) { + let mut seen_values: HashSet = HashSet::default(); + for value in iter { + while seen_values.contains(value.as_str()) { + value.push('1'); + } + seen_values.insert(value.clone()); + } +} + +proptest::prop_compose! { + pub(super) fn arb_field_from_integer(bit_size: u32)(value: u128)-> FieldElement { + let width = (bit_size % 128).clamp(1, 127); + let max_value = 2u128.pow(width) - 1; + FieldElement::from(value.clamp(0, max_value)) + } +} + +fn arb_value_from_abi_type(abi_type: &AbiType) -> SBoxedStrategy { + match abi_type { + AbiType::Field => vec(any::(), 32) + .prop_map(|bytes| InputValue::Field(FieldElement::from_be_bytes_reduce(&bytes))) + .sboxed(), + AbiType::Integer { width, .. } => { + arb_field_from_integer(*width).prop_map(InputValue::Field).sboxed() + } + + AbiType::Boolean => { + any::().prop_map(|val| InputValue::Field(FieldElement::from(val))).sboxed() + } + + AbiType::String { length } => { + // Strings only allow ASCII characters as each character must be able to be represented by a single byte. + let string_regex = format!("[[:ascii:]]{{{length}}}"); + proptest::string::string_regex(&string_regex) + .expect("parsing of regex should always succeed") + .prop_map(InputValue::String) + .sboxed() + } + AbiType::Array { length, typ } => { + let length = *length as usize; + let elements = vec(arb_value_from_abi_type(typ), length..=length); + + elements.prop_map(InputValue::Vec).sboxed() + } + + AbiType::Struct { fields, .. } => { + let fields: Vec> = fields + .iter() + .map(|(name, typ)| (Just(name.clone()), arb_value_from_abi_type(typ)).sboxed()) + .collect(); + + fields + .prop_map(|fields| { + let fields: BTreeMap<_, _> = fields.into_iter().collect(); + InputValue::Struct(fields) + }) + .sboxed() + } + + AbiType::Tuple { fields } => { + let fields: Vec<_> = fields.iter().map(arb_value_from_abi_type).collect(); + fields.prop_map(InputValue::Vec).sboxed() + } + } +} + +fn arb_primitive_abi_type() -> SBoxedStrategy { + const MAX_STRING_LEN: u32 = 1000; + proptest::prop_oneof![ + Just(AbiType::Field), + Just(AbiType::Boolean), + any::<(Sign, u32)>().prop_map(|(sign, width)| { + let width = (width % 128).clamp(1, 127); + AbiType::Integer { sign, width } + }), + // restrict length of strings to avoid running out of memory + (1..MAX_STRING_LEN).prop_map(|length| AbiType::String { length }), + ] + .sboxed() +} + +pub(super) fn arb_abi_type() -> BoxedStrategy { + let leaf = arb_primitive_abi_type(); + + leaf.prop_recursive( + 8, // up to 8 levels deep + 256, // Shoot for maximum size of 256 nodes + 10, // We put up to 10 items per collection + |inner| { + prop_oneof![ + (1..10u32, inner.clone()) + .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) + .boxed(), + prop::collection::vec(inner.clone(), 1..10) + .prop_map(|fields| { AbiType::Tuple { fields } }) + .boxed(), + (".*", prop::collection::vec((".+", inner), 1..10)) + .prop_map(|(path, mut fields)| { + // Require that all field names are unique. + ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); + AbiType::Struct { path, fields } + }) + .boxed(), + ] + }, + ) + .boxed() +} + +fn arb_abi_param_and_value() -> BoxedStrategy<(AbiParameter, InputValue)> { + arb_abi_type() + .prop_flat_map(|typ| { + let value = arb_value_from_abi_type(&typ); + let param = arb_abi_param(typ); + (param, value) + }) + .boxed() +} + +fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { + (".+", any::()) + .prop_map(move |(name, visibility)| AbiParameter { name, typ: typ.clone(), visibility }) + .sboxed() +} + +prop_compose! { + pub(super) fn arb_abi_and_input_map() + (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + -> (Abi, InputMap) { + // Require that all parameter names are unique. + ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); + + let parameters = vecmap(¶meters_with_values, |(param, _)| param.clone()); + let input_map = btree_map(parameters_with_values, |(param, value)| (param.name, value)); + + (Abi { parameters, return_type, error_types: BTreeMap::default() }, input_map) + } +} diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 2ebccbad1a0..b3d80099137 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -25,6 +25,9 @@ use std::{collections::BTreeMap, str}; // // This ABI has nothing to do with ACVM or ACIR. Although they implicitly have a relationship +#[cfg(test)] +mod arbitrary; + pub mod errors; pub mod input_parser; mod serialization; @@ -75,6 +78,7 @@ pub enum AbiType { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] #[serde(rename_all = "lowercase")] /// Represents whether the parameter is public or known only to the prover. pub enum AbiVisibility { @@ -86,6 +90,7 @@ pub enum AbiVisibility { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] #[serde(rename_all = "lowercase")] pub enum Sign { Unsigned, @@ -142,10 +147,12 @@ impl From<&AbiType> for PrintableType { } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] /// An argument or return value of the circuit's `main` function. pub struct AbiParameter { pub name: String, #[serde(rename = "type")] + #[cfg_attr(test, proptest(strategy = "arbitrary::arb_abi_type()"))] pub typ: AbiType, pub visibility: AbiVisibility, } @@ -157,16 +164,20 @@ impl AbiParameter { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] pub struct AbiReturnType { + #[cfg_attr(test, proptest(strategy = "arbitrary::arb_abi_type()"))] pub abi_type: AbiType, pub visibility: AbiVisibility, } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] pub struct Abi { /// An ordered list of the arguments to the program's `main` function, specifying their types and visibility. pub parameters: Vec, pub return_type: Option, + #[cfg_attr(test, proptest(strategy = "proptest::prelude::Just(BTreeMap::from([]))"))] pub error_types: BTreeMap, } @@ -488,56 +499,18 @@ pub fn display_abi_error( #[cfg(test)] mod test { - use std::collections::BTreeMap; - - use acvm::{AcirField, FieldElement}; + use proptest::prelude::*; - use crate::{ - input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, - InputMap, - }; + use crate::arbitrary::arb_abi_and_input_map; - #[test] - fn witness_encoding_roundtrip() { - let abi = Abi { - parameters: vec![ - AbiParameter { - name: "thing1".to_string(), - typ: AbiType::Array { length: 2, typ: Box::new(AbiType::Field) }, - visibility: AbiVisibility::Public, - }, - AbiParameter { - name: "thing2".to_string(), - typ: AbiType::Field, - visibility: AbiVisibility::Public, - }, - ], - return_type: Some(AbiReturnType { - abi_type: AbiType::Field, - visibility: AbiVisibility::Public, - }), - error_types: BTreeMap::default(), - }; + proptest! { + #[test] + fn encoding_and_decoding_returns_original_witness_map((abi, input_map) in arb_abi_and_input_map()) { + let witness_map = abi.encode(&input_map, None).unwrap(); + let (decoded_inputs, return_value) = abi.decode(&witness_map).unwrap(); - // Note we omit return value from inputs - let inputs: InputMap = BTreeMap::from([ - ( - "thing1".to_string(), - InputValue::Vec(vec![ - InputValue::Field(FieldElement::one()), - InputValue::Field(FieldElement::one()), - ]), - ), - ("thing2".to_string(), InputValue::Field(FieldElement::zero())), - ]); - - let witness_map = abi.encode(&inputs, None).unwrap(); - let (reconstructed_inputs, return_value) = abi.decode(&witness_map).unwrap(); - - for (key, expected_value) in inputs { - assert_eq!(reconstructed_inputs[&key], expected_value); + prop_assert_eq!(decoded_inputs, input_map); + prop_assert_eq!(return_value, None); } - - assert!(return_value.is_none()); } } From 1c19dfff286f1a4d65b95c907e21d1730d40d270 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:50:02 +0100 Subject: [PATCH 27/57] chore: redo typo PR by dropbigfish (#5234) Thanks dropbigfish for https://github.com/noir-lang/noir/pull/5232. Our policy is to redo typo changes to dissuade metric farming. This is an automated script. Signed-off-by: dropbigfish Co-authored-by: dropbigfish --- compiler/noirc_evaluator/src/ssa.rs | 2 +- .../noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs | 2 +- compiler/noirc_frontend/src/elaborator/mod.rs | 2 +- compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs | 2 +- compiler/noirc_frontend/src/lexer/token.rs | 2 +- docs/versioned_docs/version-v0.28.0/tutorials/noirjs_app.md | 2 +- docs/versioned_docs/version-v0.29.0/tutorials/noirjs_app.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index f7b6dfd3771..7e362599fb5 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -146,7 +146,7 @@ impl SsaProgramArtifact { /// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program]. /// -/// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation. +/// The output ACIR is backend-agnostic and so must go through a transformation pass before usage in proof generation. #[allow(clippy::type_complexity)] #[tracing::instrument(level = "trace", skip_all)] pub fn create_program( diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index c59134e4ecc..de75d34565e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -304,7 +304,7 @@ impl<'a> ValueMerger<'a> { let mut current_then = then_value; let mut current_else = else_value; - // Arbitrarily limit this to looking at at most 10 past ArraySet operations. + // Arbitrarily limit this to looking at most 10 past ArraySet operations. // If there are more than that, we assume 2 completely separate arrays are being merged. let max_iters = 2; let mut seen_then = Vec::with_capacity(max_iters); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 3b8158667f9..7bdb7d2886b 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -247,7 +247,7 @@ impl<'context> Elaborator<'context> { } // We must wait to resolve non-literal globals until after we resolve structs since struct - // globals will need to reference the struct type they're initialized to to ensure they are valid. + // globals will need to reference the struct type they're initialized to ensure they are valid. while let Some((_, global)) = this.unresolved_globals.pop_first() { this.elaborate_global(global); } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 9b47a104a40..4511537a1cc 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -404,7 +404,7 @@ impl DefCollector { resolved_module.errors.extend(collect_impls(context, crate_id, &def_collector.items.impls)); // We must wait to resolve non-integer globals until after we resolve structs since struct - // globals will need to reference the struct type they're initialized to to ensure they are valid. + // globals will need to reference the struct type they're initialized to ensure they are valid. resolved_module.resolve_globals(context, other_globals, crate_id); // Resolve each function in the crate. This is now possible since imports have been resolved diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 2199333e90f..d204191796c 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -460,7 +460,7 @@ impl fmt::Display for IntType { impl IntType { // XXX: Result - // Is not the best API. We could split this into two functions. One that checks if the the + // Is not the best API. We could split this into two functions. One that checks if the // word is a integer, which only returns an Option pub(crate) fn lookup_int_type(word: &str) -> Result, LexerErrorKind> { // Check if the first string is a 'u' or 'i' diff --git a/docs/versioned_docs/version-v0.28.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.28.0/tutorials/noirjs_app.md index 6446e0b2a76..3dd9fe7d2b0 100644 --- a/docs/versioned_docs/version-v0.28.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.28.0/tutorials/noirjs_app.md @@ -24,7 +24,7 @@ Before we start, we want to make sure we have Node and Nargo installed. We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). -As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: +As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: ```sh curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash diff --git a/docs/versioned_docs/version-v0.29.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.29.0/tutorials/noirjs_app.md index 6446e0b2a76..3dd9fe7d2b0 100644 --- a/docs/versioned_docs/version-v0.29.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.29.0/tutorials/noirjs_app.md @@ -24,7 +24,7 @@ Before we start, we want to make sure we have Node and Nargo installed. We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). -As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: +As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: ```sh curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash From a2816dbf7f9d31967fc95205a43fdfdf181029b0 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 13 Jun 2024 09:56:27 -0500 Subject: [PATCH 28/57] fix: Disable `if` optimization (#5240) # Description ## Problem\* Resolves #5202 ## Summary\* The linked issue was coming from our if condition optimization where we assume the value of an if's condition to be true within the then branch and false within the else branch. It seems the `map_value` calls from this weren't being reset properly since the nested `if f1.sign { .. } { .. }`'s else branch assumed `f1.sign` to be false, which was not reset properly after the `if`. I've temporarily disabled the optimization until we can add it back without breaking this test. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/opt/flatten_cfg.rs | 27 +-- .../regression_5202/Nargo.toml | 8 + .../regression_5202/fraction/LICENSE | 21 ++ .../regression_5202/fraction/Nargo.toml | 6 + .../regression_5202/fraction/README.md | 10 + .../regression_5202/fraction/src/lib.nr | 201 ++++++++++++++++++ .../regression_5202/src/main.nr | 23 ++ 7 files changed, 270 insertions(+), 26 deletions(-) create mode 100644 test_programs/execution_success/regression_5202/Nargo.toml create mode 100644 test_programs/execution_success/regression_5202/fraction/LICENSE create mode 100644 test_programs/execution_success/regression_5202/fraction/Nargo.toml create mode 100644 test_programs/execution_success/regression_5202/fraction/README.md create mode 100644 test_programs/execution_success/regression_5202/fraction/src/lib.nr create mode 100644 test_programs/execution_success/regression_5202/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index e07f947db8d..58f70ba9192 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -375,7 +375,6 @@ impl<'f> Context<'f> { let old_condition = *condition; let then_condition = self.inserter.resolve(old_condition); - let one = FieldElement::one(); let old_stores = std::mem::take(&mut self.store_values); let old_allocations = std::mem::take(&mut self.local_allocations); let branch = ConditionalBranch { @@ -393,15 +392,6 @@ impl<'f> Context<'f> { }; self.condition_stack.push(cond_context); self.insert_current_side_effects_enabled(); - // Optimization: within the then branch we know the condition to be true, so replace - // any references of it within this branch with true. Likewise, do the same with false - // with the else branch. We must be careful not to replace the condition if it is a - // known constant, otherwise we can end up setting 1 = 0 or vice-versa. - if self.inserter.function.dfg.get_numeric_constant(old_condition).is_none() { - let known_value = self.inserter.function.dfg.make_constant(one, Type::bool()); - - self.inserter.map_value(old_condition, known_value); - } vec![self.branch_ends[if_entry], *else_destination, *then_destination] } @@ -414,7 +404,6 @@ impl<'f> Context<'f> { self.insert_instruction(Instruction::Not(cond_context.condition), CallStack::new()); let else_condition = self.link_condition(else_condition); - let zero = FieldElement::zero(); // Make sure the else branch sees the previous values of each store // rather than any values created in the 'then' branch. let old_stores = std::mem::take(&mut cond_context.then_branch.store_values); @@ -429,21 +418,12 @@ impl<'f> Context<'f> { local_allocations: old_allocations, last_block: *block, }; - let old_condition = else_branch.old_condition; cond_context.then_branch.local_allocations.clear(); cond_context.else_branch = Some(else_branch); self.condition_stack.push(cond_context); self.insert_current_side_effects_enabled(); - // Optimization: within the then branch we know the condition to be true, so replace - // any references of it within this branch with true. Likewise, do the same with false - // with the else branch. We must be careful not to replace the condition if it is a - // known constant, otherwise we can end up setting 1 = 0 or vice-versa. - if self.inserter.function.dfg.get_numeric_constant(old_condition).is_none() { - let known_value = self.inserter.function.dfg.make_constant(zero, Type::bool()); - - self.inserter.map_value(old_condition, known_value); - } + assert_eq!(self.cfg.successors(*block).len(), 1); vec![self.cfg.successors(*block).next().unwrap()] } @@ -471,11 +451,6 @@ impl<'f> Context<'f> { // known to be true/false within the then/else branch respectively. self.insert_current_side_effects_enabled(); - // We must map back to `then_condition` here. Mapping `old_condition` to itself would - // lose any previous mappings. - self.inserter - .map_value(cond_context.then_branch.old_condition, cond_context.then_branch.condition); - // While there is a condition on the stack we don't compile outside the condition // until it is popped. This ensures we inline the full then and else branches // before continuing from the end of the conditional here where they can be merged properly. diff --git a/test_programs/execution_success/regression_5202/Nargo.toml b/test_programs/execution_success/regression_5202/Nargo.toml new file mode 100644 index 00000000000..da3da06a306 --- /dev/null +++ b/test_programs/execution_success/regression_5202/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "regression_5202" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] +fraction = { path = "fraction" } diff --git a/test_programs/execution_success/regression_5202/fraction/LICENSE b/test_programs/execution_success/regression_5202/fraction/LICENSE new file mode 100644 index 00000000000..929af4807ca --- /dev/null +++ b/test_programs/execution_success/regression_5202/fraction/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Resurgence Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test_programs/execution_success/regression_5202/fraction/Nargo.toml b/test_programs/execution_success/regression_5202/fraction/Nargo.toml new file mode 100644 index 00000000000..82e929d0bc7 --- /dev/null +++ b/test_programs/execution_success/regression_5202/fraction/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "fraction" +type = "lib" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/regression_5202/fraction/README.md b/test_programs/execution_success/regression_5202/fraction/README.md new file mode 100644 index 00000000000..17c81e5555a --- /dev/null +++ b/test_programs/execution_success/regression_5202/fraction/README.md @@ -0,0 +1,10 @@ +# fraction +The Noir repository for accessing Fractions by Resurgence Labs + +# usage +To Use as a Dependency, add the following line to your project's Nargo.toml under [dependencies]: +fraction = { tag = "v1.2.2", git = "https://github.com/resurgencelabs/fraction" } + +Note: This is a highly dynamic repository. The code will change as more features are added, both at the repository level by Resurgence Labs, as well as at the language level itself by the Noir team. + + diff --git a/test_programs/execution_success/regression_5202/fraction/src/lib.nr b/test_programs/execution_success/regression_5202/fraction/src/lib.nr new file mode 100644 index 00000000000..4b20eb6b76c --- /dev/null +++ b/test_programs/execution_success/regression_5202/fraction/src/lib.nr @@ -0,0 +1,201 @@ +use dep::std; + +global MAX: Fraction = Fraction { sign: true, num: 3050913689, den: 1 }; +global MIN: Fraction = Fraction { sign: false, num: 3050913689, den: 1 }; +global ZERO: Fraction = Fraction { sign: true, num: 0, den: 1 }; +global ONE: Fraction = Fraction { sign: true, num: 1, den: 1 }; +global NEGATIVE_ONE: Fraction = Fraction { sign: false, num: 1, den: 1 }; + +struct Fraction { + sign: bool, + num: u32, + den: u32, +} + +// Create a Fraction type variable without lenghtier code +pub fn toFraction(s: bool, n: u32, d: u32) -> Fraction { + assert(d != 0); + Fraction { sign: s, num: n, den: d } +} + +// Swaps the numerator and denominator +fn invertFraction(f: Fraction) -> Fraction { + assert(f.num != 0); + Fraction { sign: f.sign, num: f.den, den: f.num } +} + +// Changes the Sign of the Fraction +fn signChangeFraction(f: Fraction) -> Fraction { + Fraction { sign: !f.sign, num: f.num, den: f.den } +} + +// this method will only work till numerator and denominator values are under 100 +// this has been set for efficiency reasons, and will be modified once the Noir team +// can implement dynamic limit for loops +fn reduceFraction(f: Fraction) -> Fraction { + let mut a = f.num; + let mut b = f.den; + let mut j = 1; + let mut gcd = 1; + + let min = if a > b { b } else { a }; + + for i in 2..100 { + j = i as u32; + if (j <= min) { + if (a % j == 0) & (b % j == 0) { + gcd = j; + } + } + } + + Fraction { sign: f.sign, num: f.num / gcd, den: f.den / gcd } +} + +// Adds two fractions +pub fn addFraction(f1: Fraction, f2: Fraction) -> Fraction { + let mut an = U128::from_integer(f1.num); + let mut ad = U128::from_integer(f1.den); + let mut bn = U128::from_integer(f2.num); + let mut bd = U128::from_integer(f2.den); + let mut m = f1; + let mut n = f2; + + if f1.sign == f2.sign { + if ((ad * bd > U128::from_integer(2000000000)) + | ((an * bd + ad * bn) > U128::from_integer(2000000000)) + | ((an * bd) > U128::from_integer(2000000000)) + | ((ad * bn) > U128::from_integer(2000000000))) { + m = reduceFraction(m); + n = reduceFraction(n); + } + an = U128::from_integer(m.num); + ad = U128::from_integer(m.den); + bn = U128::from_integer(n.num); + bd = U128::from_integer(n.den); + if ((ad * bd > U128::from_integer(2000000000)) + | ((an * bd + ad * bn) > U128::from_integer(2000000000)) + | ((an * bd) > U128::from_integer(2000000000)) + | ((ad * bn) > U128::from_integer(2000000000))) { + let mut ddd = (an * bd + ad * bn) / (ad * bd); + let mut factor = U128::from_integer(1); + for _ in 1..5 { + if ddd * U128::from_integer(10) < U128::from_integer(2000000000) { + ddd *= U128::from_integer(10); + factor *= U128::from_integer(10); + } + } + let np: u32 = U128::to_integer(((an * bd + ad * bn) * factor) / (ad * bd)); + let fx: u32 = U128::to_integer(factor); + Fraction { sign: f1.sign, num: np, den: fx } + } else { + Fraction { sign: f1.sign, num: (m.num * n.den + n.num * m.den), den: m.den * n.den } + } + } else if ((an * bd) > (bn * ad)) { + if ((ad * bd > U128::from_integer(2000000000)) + | ((an * bd - ad * bn) > U128::from_integer(2000000000)) + | ((an * bd) > U128::from_integer(2000000000))) { + m = reduceFraction(m); + n = reduceFraction(n); + } + an = U128::from_integer(m.num); + ad = U128::from_integer(m.den); + bn = U128::from_integer(n.num); + bd = U128::from_integer(n.den); + + if ((ad * bd > U128::from_integer(2000000000)) + | ((an * bd - ad * bn) > U128::from_integer(2000000000)) + | ((an * bd) > U128::from_integer(2000000000))) { + let mut ddd = (an * bd - ad * bn) / (ad * bd); + let mut factor = U128::from_integer(1); + for _ in 1..5 { + if ddd * U128::from_integer(10) < U128::from_integer(2000000000) { + ddd *= U128::from_integer(10); + factor *= U128::from_integer(10); + } + } + let np: u32 = U128::to_integer(((an * bd - ad * bn) * factor) / (ad * bd)); + let fx: u32 = U128::to_integer(factor); + Fraction { sign: f1.sign, num: np, den: fx } + } else { + Fraction { sign: f1.sign, num: (m.num * n.den - n.num * m.den), den: m.den * n.den } + } + } else { + if ((ad * bd > U128::from_integer(2000000000)) + | ((bn * ad - bd * an) > U128::from_integer(2000000000)) + | ((bn * ad) > U128::from_integer(2000000000))) { + m = reduceFraction(m); + n = reduceFraction(n); + } + an = U128::from_integer(m.num); + ad = U128::from_integer(m.den); + bn = U128::from_integer(n.num); + bd = U128::from_integer(n.den); + if ((ad * bd > U128::from_integer(2000000000)) + | ((bn * ad - bd * an) > U128::from_integer(2000000000)) + | ((bn * ad) > U128::from_integer(2000000000))) { + let mut ddd = (bn * ad - bd * an) / (ad * bd); + let mut factor = U128::from_integer(1); + for _ in 1..5 { + if ddd * U128::from_integer(10) < U128::from_integer(2000000000) { + ddd *= U128::from_integer(10); + factor *= U128::from_integer(10); + } + } + let np: u32 = U128::to_integer(((bn * ad - bd * an) * factor) / (ad * bd)); + let fx: u32 = U128::to_integer(factor); + Fraction { sign: f2.sign, num: np, den: fx } + } else { + Fraction { sign: f2.sign, num: (n.num * m.den - m.num * n.den), den: m.den * n.den } + } + } +} + +// Returns the closest but smaller Integer to the Given Fraction, but typecast to Fraction for convenience +pub fn floor(f: Fraction) -> Fraction { + let q = f.num / f.den; + if q * f.den == f.num { + Fraction { sign: f.sign, num: f.num, den: f.den } + } else if f.sign { + Fraction { sign: f.sign, num: q, den: 1 } + } else { + Fraction { sign: f.sign, num: q + 1, den: 1 } + } +} + +#[test] +fn test_sum() { + let f1 = toFraction(true, 3, 5); + let f2 = toFraction(true, 2, 5); + let f = addFraction(f1, f2); + assert(f.num == f.den); +} + +#[test] +fn test_reduce() { + let f1 = toFraction(true, 2, 10); + let f2 = reduceFraction(f1); + assert(f2.num == 1); +} + +#[test] +fn test_floor() { + let f = toFraction(true, 7, 5); + let fl = floor(f); + assert(fl.num == 1); + assert(fl.den == 1); +} + +#[test] +fn test_floor2() { + let f = toFraction(false, 12, 5); + let fl = floor(f); + assert(fl.num == 3); + assert(fl.den == 1); +} + +#[test] +fn test_globals() { + let a = addFraction(ONE, NEGATIVE_ONE); + assert(a.num == ZERO.num); +} diff --git a/test_programs/execution_success/regression_5202/src/main.nr b/test_programs/execution_success/regression_5202/src/main.nr new file mode 100644 index 00000000000..e41b760b83e --- /dev/null +++ b/test_programs/execution_success/regression_5202/src/main.nr @@ -0,0 +1,23 @@ +use dep::fraction::{Fraction, MAX, floor, toFraction, addFraction}; + +fn main() { + let g1 = toFraction(true, 33333333, 5); + let g2 = toFraction(true, 500000, 33333333); + let a = addFraction(g1, g2); + + let f1 = floor(a); + let f2 = MAX; + assert(f1.sign); + assert(f2.sign); + + if f1.sign != f2.sign { + if (f1.sign) { () } else { () } + } else { + // Test fails here before the fix to #5202. + // An optimization which assumes an if condition to be true/false + // within the then/else branch respectively wasn't being set properly + // causing f1.sign to be assumed to be false in this else branch. + assert(f1.sign); + assert(f2.sign); + } +} From 1849389362e22e8236177f84b735dadf840cd637 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:06:40 +0100 Subject: [PATCH 29/57] feat: add `set` and `set_unchecked` methods to `Vec` and `BoundedVec` (#5241) # Description ## Problem\* Resolves ## Summary\* This PR exposes `set` and `set_unchecked` methods to `BoundedVec` which allow writing to individual indices in the array without having to access `self.storage` directly. I've also documented the expected behaviour for the get and set operations. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../standard_library/containers/boundedvec.md | 40 ++++++++- .../noir/standard_library/containers/vec.mdx | 19 +++++ noir_stdlib/src/collections/bounded_vec.nr | 59 ++++++++++++- noir_stdlib/src/collections/vec.nr | 39 +++++++++ .../noir_test_success/bounded_vec/src/main.nr | 27 ++++++ tooling/nargo_cli/tests/stdlib-tests.rs | 85 ++++++++++++++++++- 6 files changed, 263 insertions(+), 6 deletions(-) diff --git a/docs/docs/noir/standard_library/containers/boundedvec.md b/docs/docs/noir/standard_library/containers/boundedvec.md index ccce62562f8..c476c877d87 100644 --- a/docs/docs/noir/standard_library/containers/boundedvec.md +++ b/docs/docs/noir/standard_library/containers/boundedvec.md @@ -59,7 +59,7 @@ but for now make sure to use type annotations when using bounded vectors. Otherw ### get ```rust -pub fn get(mut self: Self, index: u64) -> T { +pub fn get(self, index: u64) -> T { ``` Retrieves an element from the vector at the given index, starting from zero. @@ -80,7 +80,7 @@ fn foo(v: BoundedVec) { ### get_unchecked ```rust -pub fn get_unchecked(mut self: Self, index: u64) -> T { +pub fn get_unchecked(self, index: u64) -> T { ``` Retrieves an element from the vector at the given index, starting from zero, without @@ -93,6 +93,42 @@ Example: #include_code get_unchecked_example test_programs/noir_test_success/bounded_vec/src/main.nr rust +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + assert(first != 42); + v.set(0, 42); + let new_first = v.get(0); + assert(new_first == 42); +} +``` + +### set_unchecked + +```rust +pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { +``` + +Writes an element to the vector at the given index, starting from zero, without performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! + +Example: + +#include_code set_unchecked_example test_programs/noir_test_success/bounded_vec/src/main.nr rust + ### push diff --git a/docs/docs/noir/standard_library/containers/vec.mdx b/docs/docs/noir/standard_library/containers/vec.mdx index fcfd7e07aa0..475011922f8 100644 --- a/docs/docs/noir/standard_library/containers/vec.mdx +++ b/docs/docs/noir/standard_library/containers/vec.mdx @@ -84,6 +84,25 @@ let vector: Vec = Vec::from_slice(&[10, 20, 30]); assert(vector.get(1) == 20); ``` +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +Panics if the index points beyond the vector's end. + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +vector.set(1, 42); +assert(vector.get(1) == 42); +``` + ### push Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index aae96e5943d..801fe3da75a 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -11,15 +11,35 @@ impl BoundedVec { BoundedVec { storage: [zeroed; MaxLen], len: 0 } } - pub fn get(mut self: Self, index: u32) -> T { + /// Get an element from the vector at the given index. + /// Panics if the given index points beyond the end of the vector (`self.len()`). + pub fn get(self, index: u32) -> T { assert(index < self.len); - self.storage[index] + self.get_unchecked(index) } - pub fn get_unchecked(mut self: Self, index: u32) -> T { + /// Get an element from the vector at the given index. + /// Responds with undefined data for `index` where `self.len < index < self.max_len()`. + pub fn get_unchecked(self, index: u32) -> T { self.storage[index] } + /// Write an element to the vector at the given index. + /// Panics if the given index points beyond the end of the vector (`self.len()`). + pub fn set(&mut self, index: u32, value: T) { + assert(index < self.len, "Attempted to write past end of BoundedVec"); + self.set_unchecked(index, value) + } + + /// Write an element to the vector at the given index. + /// Does not check whether the passed `index` is a valid index within the vector. + /// + /// Silently writes past the end of the vector for `index` where `self.len < index < self.max_len()` + /// Panics if the given index points beyond the maximum length of the vector (`self.max_len()`). + pub fn set_unchecked(&mut self, index: u32, value: T) { + self.storage[index] = value; + } + pub fn push(&mut self, elem: T) { assert(self.len < MaxLen, "push out of bounds"); @@ -142,6 +162,39 @@ mod bounded_vec_tests { assert(bounded_vec1 != bounded_vec2); } + mod set { + use crate::collections::bounded_vec::BoundedVec; + + #[test] + fn set_updates_values_properly() { + let mut vec = BoundedVec::from_array([0, 0, 0, 0, 0]); + + vec.set(0, 42); + assert_eq(vec.storage, [42, 0, 0, 0, 0]); + + vec.set(1, 43); + assert_eq(vec.storage, [42, 43, 0, 0, 0]); + + vec.set(2, 44); + assert_eq(vec.storage, [42, 43, 44, 0, 0]); + + vec.set(1, 10); + assert_eq(vec.storage, [42, 10, 44, 0, 0]); + + vec.set(0, 0); + assert_eq(vec.storage, [0, 10, 44, 0, 0]); + } + + #[test(should_fail_with = "Attempted to write past end of BoundedVec")] + fn panics_when_writing_elements_past_end_of_vec() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.set(0, 42); + + // Need to use println to avoid DIE removing the write operation. + crate::println(vec.get(0)); + } + } + mod from_array { use crate::collections::bounded_vec::BoundedVec; diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index 18aaa8b9b3b..cedae7f5ce1 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -21,6 +21,12 @@ impl Vec { self.slice[index] } + /// Write an element to the vector at the given index. + /// Panics if the given index points beyond the end of the vector (`self.len()`). + pub fn set(&mut self, index: u32, value: T) { + self.slice[index] = value; + } + /// Push a new element to the end of the vector, returning a /// new vector with a length one greater than the /// original unmodified vector. @@ -57,3 +63,36 @@ impl Vec { self.slice.len() } } + +mod tests { + use crate::collections::vec::Vec; + + #[test] + fn set_updates_values_properly() { + let mut vec = Vec { slice: &[0, 0, 0, 0, 0] }; + + vec.set(0, 42); + assert_eq(vec.slice, &[42, 0, 0, 0, 0]); + + vec.set(1, 43); + assert_eq(vec.slice, &[42, 43, 0, 0, 0]); + + vec.set(2, 44); + assert_eq(vec.slice, &[42, 43, 44, 0, 0]); + + vec.set(1, 10); + assert_eq(vec.slice, &[42, 10, 44, 0, 0]); + + vec.set(0, 0); + assert_eq(vec.slice, &[0, 10, 44, 0, 0]); + } + + #[test(should_fail)] + fn panics_when_writing_elements_past_end_of_vec() { + let mut vec = Vec::new(); + vec.set(0, 42); + + // Need to use println to avoid DIE removing the write operation. + crate::println(vec.get(0)); + } +} diff --git a/test_programs/noir_test_success/bounded_vec/src/main.nr b/test_programs/noir_test_success/bounded_vec/src/main.nr index 22ec291f9d6..e5aa5f88a94 100644 --- a/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -63,6 +63,33 @@ fn sum_of_first_three(v: BoundedVec) -> u32 { } // docs:end:get_unchecked_example +#[test(should_fail)] +// docs:start:set_unchecked_example +fn set_unchecked_example() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2]); + + // Here we're safely writing within the valid range of `vec` + // `vec` now has the value [42, 2] + vec.set_unchecked(0, 42); + + // We can then safely read this value back out of `vec`. + // Notice that we use the checked version of `get` which would prevent reading unsafe values. + assert_eq(vec.get(0), 42); + + // We've now written past the end of `vec`. + // As this index is still within the maximum potential length of `v`, + // it won't cause a constraint failure. + vec.set_unchecked(2, 42); + println(vec); + + // This will write past the end of the maximum potential length of `vec`, + // it will then trigger a constraint failure. + vec.set_unchecked(5, 42); + println(vec); +} +// docs:end:set_unchecked_example + #[test(should_fail_with = "push out of bounds")] fn push_docs_example() { // docs:start:bounded-vec-push-example diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs index 0bb967e7502..70857b4b65e 100644 --- a/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -1,6 +1,8 @@ +use std::io::Write; use std::{collections::BTreeMap, path::PathBuf}; use acvm::blackbox_solver::StubbedBlackBoxSolver; +use fm::FileManager; use noirc_driver::{check_crate, file_manager_with_stdlib, CompileOptions}; use noirc_frontend::hir::FunctionNameMatch; @@ -9,6 +11,7 @@ use nargo::{ package::{Package, PackageType}, parse_all, prepare_package, }; +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; #[test] fn run_stdlib_tests() { @@ -23,7 +26,7 @@ fn run_stdlib_tests() { root_dir: PathBuf::from("."), package_type: PackageType::Binary, entry_path: PathBuf::from("main.nr"), - name: "dummy".parse().unwrap(), + name: "stdlib".parse().unwrap(), dependencies: BTreeMap::new(), }; @@ -58,5 +61,85 @@ fn run_stdlib_tests() { .collect(); assert!(!test_report.is_empty(), "Could not find any tests within the stdlib"); + display_test_report(&file_manager, &dummy_package, &CompileOptions::default(), &test_report); assert!(test_report.iter().all(|(_, status)| !status.failed())); } + +// This code is copied from `src/cli/test_cmd.rs`. +// This should be abstracted into a proper test runner at some point. +fn display_test_report( + file_manager: &FileManager, + package: &Package, + compile_options: &CompileOptions, + test_report: &[(String, TestStatus)], +) { + let writer = StandardStream::stderr(ColorChoice::Always); + let mut writer = writer.lock(); + + for (test_name, test_status) in test_report { + write!(writer, "[{}] Testing {test_name}... ", package.name) + .expect("Failed to write to stderr"); + writer.flush().expect("Failed to flush writer"); + + match &test_status { + TestStatus::Pass { .. } => { + writer + .set_color(ColorSpec::new().set_fg(Some(Color::Green))) + .expect("Failed to set color"); + writeln!(writer, "ok").expect("Failed to write to stderr"); + } + TestStatus::Fail { message, error_diagnostic } => { + writer + .set_color(ColorSpec::new().set_fg(Some(Color::Red))) + .expect("Failed to set color"); + writeln!(writer, "FAIL\n{message}\n").expect("Failed to write to stderr"); + if let Some(diag) = error_diagnostic { + noirc_errors::reporter::report_all( + file_manager.as_file_map(), + &[diag.clone()], + compile_options.deny_warnings, + compile_options.silence_warnings, + ); + } + } + TestStatus::CompileError(err) => { + noirc_errors::reporter::report_all( + file_manager.as_file_map(), + &[err.clone()], + compile_options.deny_warnings, + compile_options.silence_warnings, + ); + } + } + writer.reset().expect("Failed to reset writer"); + } + + write!(writer, "[{}] ", package.name).expect("Failed to write to stderr"); + + let count_all = test_report.len(); + let count_failed = test_report.iter().filter(|(_, status)| status.failed()).count(); + let plural = if count_all == 1 { "" } else { "s" }; + if count_failed == 0 { + writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).expect("Failed to set color"); + write!(writer, "{count_all} test{plural} passed").expect("Failed to write to stderr"); + writer.reset().expect("Failed to reset writer"); + writeln!(writer).expect("Failed to write to stderr"); + } else { + let count_passed = count_all - count_failed; + let plural_failed = if count_failed == 1 { "" } else { "s" }; + let plural_passed = if count_passed == 1 { "" } else { "s" }; + + if count_passed != 0 { + writer + .set_color(ColorSpec::new().set_fg(Some(Color::Green))) + .expect("Failed to set color"); + write!(writer, "{count_passed} test{plural_passed} passed, ",) + .expect("Failed to write to stderr"); + } + + writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).expect("Failed to set color"); + writeln!(writer, "{count_failed} test{plural_failed} failed") + .expect("Failed to write to stderr"); + writer.reset().expect("Failed to reset writer"); + } +} From c6b42edbc67ed97e0787cdf5d0b790ae197ec3ed Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:54:56 +0200 Subject: [PATCH 30/57] chore: add no predicate to poseidon2 (#5252) # Description ## Problem\* Resolves #4688 ## Summary\* Adding the no_predicates attribute to poseidon2 because hashing operations have no side-effect. ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/hash/poseidon2.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 04d922b581d..4a68925255a 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -11,7 +11,7 @@ struct Poseidon2 { } impl Poseidon2 { - + #[no_predicates] pub fn hash(input: [Field; N], message_size: u32) -> Field { if message_size == N { Poseidon2::hash_internal(input, N, false) From da1549cfb296261b273a3a64908382e7b71512ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 14 Jun 2024 17:02:17 +0100 Subject: [PATCH 31/57] feat: add BoundedVec::map (#5250) # Description This adds a `map` function to `BoundedMap`, which I've found to be a relatively common need, and quite annoying to implement outside of the stdlib as it requires raw access to the internals of the data structure. ## Documentation Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../standard_library/containers/boundedvec.md | 12 +++++ noir_stdlib/src/collections/bounded_vec.nr | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/docs/docs/noir/standard_library/containers/boundedvec.md b/docs/docs/noir/standard_library/containers/boundedvec.md index c476c877d87..98b7d584033 100644 --- a/docs/docs/noir/standard_library/containers/boundedvec.md +++ b/docs/docs/noir/standard_library/containers/boundedvec.md @@ -246,6 +246,18 @@ Example: let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) ``` +### map + +```rust +pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec +``` + +Creates a new vector of equal size by calling a closure on each element in this vector. + +Example: + +#include_code bounded-vec-map-example noir_stdlib/src/collections/bounded_vec.nr rust + ### any ```rust diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 801fe3da75a..6fde9e70f4d 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -121,6 +121,17 @@ impl BoundedVec { } ret } + + pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec { + let mut ret = BoundedVec::new(); + ret.len = self.len(); + for i in 0..MaxLen { + if i < self.len() { + ret.storage[i] = f(self.get_unchecked(i)); + } + } + ret + } } impl Eq for BoundedVec where T: Eq { @@ -195,6 +206,40 @@ mod bounded_vec_tests { } } + mod map { + use crate::collections::bounded_vec::BoundedVec; + + #[test] + fn applies_function_correctly() { + // docs:start:bounded-vec-map-example + let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + let result = vec.map(|value| value * 2); + // docs:end:bounded-vec-map-example + let expected = BoundedVec::from_array([2, 4, 6, 8]); + + assert_eq(result, expected); + } + + #[test] + fn applies_function_that_changes_return_type() { + let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + let result = vec.map(|value| (value * 2) as Field); + let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]); + + assert_eq(result, expected); + } + + #[test] + fn does_not_apply_function_past_len() { + let vec: BoundedVec = BoundedVec::from_array([0, 1]); + let result = vec.map(|value| if value == 0 { 5 } else { value }); + let expected = BoundedVec::from_array([5, 1]); + + assert_eq(result, expected); + assert_eq(result.storage()[2], 0); + } + } + mod from_array { use crate::collections::bounded_vec::BoundedVec; From d9b4712bf1a62548dd7ed17b181882ae537d70dd Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 14 Jun 2024 14:16:25 -0500 Subject: [PATCH 32/57] feat(experimental): Implement macro calls & splicing into `Expr` values (#5203) # Description ## Problem\* Resolves #4591 ## Summary\* Implements macro calls `foo!()` and splicing via the `$` operator of `Expr` values into `quote` expressions. ```rust comptime fn my_macro(x: Field, y: Field) -> Expr { quote { $x + $y + x + y } } fn main(x: Field, y: pub Field) { let result = my_macro!(1, 2); assert_eq(result, 1 + 2 + x + y); } ``` ## Additional Context This feature is elaborator-only and does not work with the resolver / type checker. I've yet to add the integration test that pairs with this as a result since I'd have to add a check to the build step to exclude it from any runs without `--use-elaborator`. I'll add this later but the PR should be ready for review at least now. ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Maxim Vezenov --- compiler/noirc_driver/src/abi_gen.rs | 2 +- compiler/noirc_frontend/src/ast/expression.rs | 11 + compiler/noirc_frontend/src/ast/mod.rs | 4 +- compiler/noirc_frontend/src/ast/statement.rs | 6 +- .../src/elaborator/expressions.rs | 104 +++++- compiler/noirc_frontend/src/elaborator/mod.rs | 1 + .../noirc_frontend/src/elaborator/types.rs | 4 +- .../noirc_frontend/src/elaborator/unquote.rs | 306 ++++++++++++++++++ .../src/hir/comptime/interpreter.rs | 20 +- .../src/hir/comptime/interpreter/unquote.rs | 287 ++++++++++++++++ .../noirc_frontend/src/hir/comptime/value.rs | 2 +- .../src/hir/resolution/errors.rs | 27 ++ .../src/hir/resolution/resolver.rs | 18 +- .../src/hir/type_check/errors.rs | 7 + .../noirc_frontend/src/hir/type_check/expr.rs | 2 +- compiler/noirc_frontend/src/hir_def/expr.rs | 12 +- compiler/noirc_frontend/src/hir_def/types.rs | 22 +- .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/node_interner.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 38 +-- .../src/parser/parser/literals.rs | 12 +- .../noirc_frontend/src/parser/parser/types.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 2 +- .../compile_success_empty/macros/Nargo.toml | 7 + .../compile_success_empty/macros/src/main.nr | 15 + tooling/debugger/ignored-tests.txt | 3 +- tooling/nargo_cli/build.rs | 32 +- tooling/nargo_fmt/src/rewrite/expr.rs | 8 + tooling/nargo_fmt/src/rewrite/typ.rs | 2 +- 29 files changed, 877 insertions(+), 83 deletions(-) create mode 100644 compiler/noirc_frontend/src/elaborator/unquote.rs create mode 100644 compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs create mode 100644 test_programs/compile_success_empty/macros/Nargo.toml create mode 100644 test_programs/compile_success_empty/macros/src/main.nr diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index e2c7fea3c07..c71474d7731 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -103,7 +103,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) - | Type::Code + | Type::Expr | Type::Slice(_) | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index da2d3646b20..075df4ee4e3 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -34,8 +34,17 @@ pub enum ExpressionKind { Lambda(Box), Parenthesized(Box), Quote(BlockExpression, Span), + Unquote(Box), Comptime(BlockExpression, Span), + /// Unquote expressions are replaced with UnquoteMarkers when Quoted + /// expressions are resolved. Since the expression being quoted remains an + /// ExpressionKind (rather than a resolved ExprId), the UnquoteMarker must be + /// here in the AST even though it is technically HIR-only. + /// Each usize in an UnquoteMarker is an index which corresponds to the index of the + /// expression in the Hir::Quote expression list to replace it with. + UnquoteMarker(usize), + // This variant is only emitted when inlining the result of comptime // code. It is used to translate function values back into the AST while // guaranteeing they have the same instantiated type and definition id without resolving again. @@ -552,6 +561,8 @@ impl Display for ExpressionKind { Comptime(block, _) => write!(f, "comptime {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), + Unquote(expr) => write!(f, "$({expr})"), + UnquoteMarker(index) => write!(f, "${index}"), } } } diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index c09ad75818a..3492df73103 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -117,7 +117,7 @@ pub enum UnresolvedTypeData { ), // The type of quoted code for metaprogramming - Code, + Expr, Unspecified, // This is for when the user declares a variable without specifying it's type Error, @@ -216,7 +216,7 @@ impl std::fmt::Display for UnresolvedTypeData { } } MutableReference(element) => write!(f, "&mut {element}"), - Code => write!(f, "Code"), + Expr => write!(f, "Expr"), Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 57a62324064..48e34ad7fc9 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -728,7 +728,11 @@ impl Display for LValue { impl Display for Path { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let segments = vecmap(&self.segments, ToString::to_string); - write!(f, "{}::{}", self.kind, segments.join("::")) + if self.kind == PathKind::Plain { + write!(f, "{}", segments.join("::")) + } else { + write!(f, "{}::{}", self.kind, segments.join("::")) + } } } diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index c1c6945707b..a57690878dc 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -18,7 +18,7 @@ use crate::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, HirMethodReference, - HirPrefixExpression, + HirPrefixExpression, HirQuoted, }, traits::TraitConstraint, }, @@ -64,6 +64,13 @@ impl<'context> Elaborator<'context> { } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), ExpressionKind::Error => (HirExpression::Error, Type::Error), + ExpressionKind::Unquote(_) => { + self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); + (HirExpression::Error, Type::Error) + } + ExpressionKind::UnquoteMarker(index) => { + unreachable!("UnquoteMarker({index}) remaining in runtime code") + } }; let id = self.interner.push_expr(hir_expr); self.interner.push_expr_location(id, expr.span, self.file); @@ -280,10 +287,22 @@ impl<'context> Elaborator<'context> { (typ, arg, span) }); + // Avoid cloning arguments unless this is a macro call + let mut comptime_args = Vec::new(); + if call.is_macro_call { + comptime_args = arguments.clone(); + } + let location = Location::new(span, self.file); - let call = HirCallExpression { func, arguments, location }; - let typ = self.type_check_call(&call, func_type, args, span); - (HirExpression::Call(call), typ) + let hir_call = HirCallExpression { func, arguments, location }; + let typ = self.type_check_call(&hir_call, func_type, args, span); + + if call.is_macro_call { + self.call_macro(func, comptime_args, location, typ) + .unwrap_or_else(|| (HirExpression::Error, Type::Error)) + } else { + (HirExpression::Call(hir_call), typ) + } } fn elaborate_method_call( @@ -627,8 +646,11 @@ impl<'context> Elaborator<'context> { (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type))) } - fn elaborate_quote(&mut self, block: BlockExpression) -> (HirExpression, Type) { - (HirExpression::Quote(block), Type::Code) + fn elaborate_quote(&mut self, mut block: BlockExpression) -> (HirExpression, Type) { + let mut unquoted_exprs = Vec::new(); + self.find_unquoted_exprs_in_block(&mut block, &mut unquoted_exprs); + let quoted = HirQuoted { quoted_block: block, unquoted_exprs }; + (HirExpression::Quote(quoted), Type::Expr) } fn elaborate_comptime_block(&mut self, block: BlockExpression, span: Span) -> (ExprId, Type) { @@ -661,4 +683,74 @@ impl<'context> Elaborator<'context> { Err(error) => make_error(self, error), } } + + fn try_get_comptime_function( + &mut self, + func: ExprId, + location: Location, + ) -> Result { + match self.interner.expression(&func) { + HirExpression::Ident(ident, _generics) => { + let definition = self.interner.definition(ident.id); + if let DefinitionKind::Function(function) = definition.kind { + let meta = self.interner.function_modifiers(&function); + if meta.is_comptime { + Ok(function) + } else { + Err(ResolverError::MacroIsNotComptime { span: location.span }) + } + } else { + Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }) + } + } + _ => Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }), + } + } + + /// Call a macro function and inlines its code at the call site. + /// This will also perform a type check to ensure that the return type is an `Expr` value. + fn call_macro( + &mut self, + func: ExprId, + arguments: Vec, + location: Location, + return_type: Type, + ) -> Option<(HirExpression, Type)> { + self.unify(&return_type, &Type::Expr, || TypeCheckError::MacroReturningNonExpr { + typ: return_type.clone(), + span: location.span, + }); + + let function = match self.try_get_comptime_function(func, location) { + Ok(function) => function, + Err(error) => { + self.push_err(error); + return None; + } + }; + + let mut interpreter = Interpreter::new(self.interner, &mut self.comptime_scopes); + + let mut comptime_args = Vec::new(); + let mut errors = Vec::new(); + + for argument in arguments { + match interpreter.evaluate(argument) { + Ok(arg) => { + let location = interpreter.interner.expr_location(&argument); + comptime_args.push((arg, location)); + } + Err(error) => errors.push((error.into(), self.file)), + } + } + + if !errors.is_empty() { + self.errors.append(&mut errors); + return None; + } + + let result = interpreter.call_function(function, comptime_args, location); + let (expr_id, typ) = self.inline_comptime_value(result, location.span); + Some((self.interner.expression(&expr_id), typ)) + } } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 7bdb7d2886b..93e8b36f4e0 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -62,6 +62,7 @@ mod scope; mod statements; mod traits; mod types; +mod unquote; use fm::FileId; use iter_extended::vecmap; diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 3baa7054fc5..a9571a6669d 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -72,7 +72,7 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_type_inner(*fields); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } - Code => Type::Code, + Expr => Type::Expr, Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, @@ -1396,7 +1396,7 @@ impl<'context> Elaborator<'context> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::Code + | Type::Expr | Type::Forall(_, _) => (), Type::TraitAsType(_, _, args) => { diff --git a/compiler/noirc_frontend/src/elaborator/unquote.rs b/compiler/noirc_frontend/src/elaborator/unquote.rs new file mode 100644 index 00000000000..6767f471f16 --- /dev/null +++ b/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -0,0 +1,306 @@ +use crate::{ + ast::{ + ArrayLiteral, AssignStatement, ConstrainStatement, ConstructorExpression, IfExpression, + InfixExpression, Lambda, + }, + macros_api::{ + BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, + ForLoopStatement, ForRange, IndexExpression, LetStatement, Literal, MemberAccessExpression, + MethodCallExpression, PrefixExpression, Statement, StatementKind, + }, + node_interner::ExprId, +}; + +use super::Elaborator; + +impl<'a> Elaborator<'a> { + pub fn find_unquoted_exprs_in_block( + &mut self, + block: &mut BlockExpression, + unquoted_exprs: &mut Vec, + ) { + for statement in &mut block.statements { + self.find_unquoted_exprs_in_statement(statement, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_statement( + &mut self, + statement: &mut Statement, + unquoted_exprs: &mut Vec, + ) { + match &mut statement.kind { + StatementKind::Let(let_) => self.find_unquoted_exprs_in_let(let_, unquoted_exprs), + StatementKind::Constrain(constrain) => { + self.find_unquoted_exprs_in_constrain(constrain, unquoted_exprs); + } + StatementKind::Expression(expr) => { + self.find_unquoted_exprs_in_expr(expr, unquoted_exprs); + } + StatementKind::Assign(assign) => { + self.find_unquoted_exprs_in_assign(assign, unquoted_exprs); + } + StatementKind::For(for_) => self.find_unquoted_exprs_in_for(for_, unquoted_exprs), + StatementKind::Break => (), + StatementKind::Continue => (), + StatementKind::Comptime(comptime) => { + self.find_unquoted_exprs_in_statement(comptime, unquoted_exprs); + } + StatementKind::Semi(expr) => self.find_unquoted_exprs_in_expr(expr, unquoted_exprs), + StatementKind::Error => (), + } + } + + fn find_unquoted_exprs_in_constrain( + &mut self, + constrain: &mut ConstrainStatement, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut constrain.0, unquoted_exprs); + if let Some(msg) = constrain.1.as_mut() { + self.find_unquoted_exprs_in_expr(msg, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_let( + &mut self, + let_: &mut LetStatement, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut let_.expression, unquoted_exprs); + } + + fn find_unquoted_exprs_in_assign( + &mut self, + assign: &mut AssignStatement, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut assign.expression, unquoted_exprs); + } + + fn find_unquoted_exprs_in_for( + &mut self, + for_: &mut ForLoopStatement, + unquoted_exprs: &mut Vec, + ) { + match &mut for_.range { + ForRange::Range(start, end) => { + self.find_unquoted_exprs_in_expr(start, unquoted_exprs); + self.find_unquoted_exprs_in_expr(end, unquoted_exprs); + } + ForRange::Array(array) => { + self.find_unquoted_exprs_in_expr(array, unquoted_exprs); + } + }; + self.find_unquoted_exprs_in_expr(&mut for_.block, unquoted_exprs); + } + + fn find_unquoted_exprs_in_expr( + &mut self, + expr: &mut Expression, + unquoted_exprs: &mut Vec, + ) { + match &mut expr.kind { + ExpressionKind::Literal(literal) => { + self.find_unquoted_exprs_in_literal(literal, unquoted_exprs); + } + ExpressionKind::Block(block) => { + self.find_unquoted_exprs_in_block(block, unquoted_exprs); + } + ExpressionKind::Prefix(prefix) => { + self.find_unquoted_exprs_in_prefix(prefix, unquoted_exprs); + } + ExpressionKind::Index(index) => { + self.find_unquoted_exprs_in_index(index, unquoted_exprs); + } + ExpressionKind::Call(call) => self.find_unquoted_exprs_in_call(call, unquoted_exprs), + ExpressionKind::MethodCall(call) => { + self.find_unquoted_exprs_in_method_call(call, unquoted_exprs); + } + ExpressionKind::Constructor(constructor) => { + self.find_unquoted_exprs_in_constructor(constructor, unquoted_exprs); + } + ExpressionKind::MemberAccess(access) => { + self.find_unquoted_exprs_in_access(access, unquoted_exprs); + } + ExpressionKind::Cast(cast) => self.find_unquoted_exprs_in_cast(cast, unquoted_exprs), + ExpressionKind::Infix(infix) => { + self.find_unquoted_exprs_in_infix(infix, unquoted_exprs); + } + ExpressionKind::If(if_) => self.find_unquoted_exprs_in_if(if_, unquoted_exprs), + ExpressionKind::Variable(_, _) => (), + ExpressionKind::Tuple(tuple) => { + self.find_unquoted_exprs_in_tuple(tuple, unquoted_exprs); + } + ExpressionKind::Lambda(lambda) => { + self.find_unquoted_exprs_in_lambda(lambda, unquoted_exprs); + } + ExpressionKind::Parenthesized(expr) => { + self.find_unquoted_exprs_in_expr(expr, unquoted_exprs); + } + ExpressionKind::Quote(quote, _) => { + self.find_unquoted_exprs_in_block(quote, unquoted_exprs); + } + ExpressionKind::Comptime(block, _) => { + self.find_unquoted_exprs_in_block(block, unquoted_exprs); + } + ExpressionKind::Resolved(_) => (), + ExpressionKind::Error => (), + ExpressionKind::UnquoteMarker(_) => (), + ExpressionKind::Unquote(unquoted) => { + // Avoid an expensive clone for unquoted + let empty_expr = Expression::new(ExpressionKind::Error, unquoted.span); + let unquote = std::mem::replace(unquoted.as_mut(), empty_expr); + self.replace_unquote(expr, unquote, unquoted_exprs); + } + } + } + + fn find_unquoted_exprs_in_literal( + &mut self, + literal: &mut Literal, + unquoted_exprs: &mut Vec, + ) { + match literal { + Literal::Array(array) | Literal::Slice(array) => match array { + ArrayLiteral::Standard(elements) => { + for element in elements { + self.find_unquoted_exprs_in_expr(element, unquoted_exprs); + } + } + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_unquoted_exprs_in_expr(repeated_element, unquoted_exprs); + self.find_unquoted_exprs_in_expr(length, unquoted_exprs); + } + }, + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + fn find_unquoted_exprs_in_prefix( + &mut self, + prefix: &mut PrefixExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut prefix.rhs, unquoted_exprs); + } + + fn find_unquoted_exprs_in_index( + &mut self, + index: &mut IndexExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut index.collection, unquoted_exprs); + self.find_unquoted_exprs_in_expr(&mut index.index, unquoted_exprs); + } + + fn find_unquoted_exprs_in_call( + &mut self, + call: &mut CallExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut call.func, unquoted_exprs); + + for arg in &mut call.arguments { + self.find_unquoted_exprs_in_expr(arg, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_method_call( + &mut self, + call: &mut MethodCallExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut call.object, unquoted_exprs); + + for arg in &mut call.arguments { + self.find_unquoted_exprs_in_expr(arg, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_constructor( + &mut self, + constructor: &mut ConstructorExpression, + unquoted_exprs: &mut Vec, + ) { + for (_, field) in &mut constructor.fields { + self.find_unquoted_exprs_in_expr(field, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_access( + &mut self, + member_access: &mut MemberAccessExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut member_access.lhs, unquoted_exprs); + } + + fn find_unquoted_exprs_in_cast( + &mut self, + cast: &mut CastExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut cast.lhs, unquoted_exprs); + } + + fn find_unquoted_exprs_in_infix( + &mut self, + infix: &mut InfixExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut infix.lhs, unquoted_exprs); + self.find_unquoted_exprs_in_expr(&mut infix.rhs, unquoted_exprs); + } + + fn find_unquoted_exprs_in_if( + &mut self, + if_: &mut IfExpression, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut if_.condition, unquoted_exprs); + self.find_unquoted_exprs_in_expr(&mut if_.consequence, unquoted_exprs); + + if let Some(alternate) = if_.alternative.as_mut() { + self.find_unquoted_exprs_in_expr(alternate, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_tuple( + &mut self, + tuple: &mut [Expression], + unquoted_exprs: &mut Vec, + ) { + for field in tuple { + self.find_unquoted_exprs_in_expr(field, unquoted_exprs); + } + } + + fn find_unquoted_exprs_in_lambda( + &mut self, + lambda: &mut Lambda, + unquoted_exprs: &mut Vec, + ) { + self.find_unquoted_exprs_in_expr(&mut lambda.body, unquoted_exprs); + } + + /// Elaborate and store the unquoted expression in the given vector, then + /// replace it with an unquote expression with an UnquoteMarker expression to mark the position + /// to replace it with later. + fn replace_unquote( + &mut self, + expr: &mut Expression, + unquoted: Expression, + unquoted_exprs: &mut Vec, + ) { + let (expr_id, _) = self.elaborate_expression(unquoted); + let unquote_marker_id = unquoted_exprs.len(); + unquoted_exprs.push(expr_id); + expr.kind = ExpressionKind::UnquoteMarker(unquote_marker_id); + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index dd436144969..8cb0db3ee55 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -7,6 +7,7 @@ use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness}; +use crate::hir_def::expr::HirQuoted; use crate::{ hir_def::{ expr::{ @@ -25,13 +26,17 @@ use crate::{ Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, }; +use self::unquote::UnquoteArgs; + use super::errors::{IResult, InterpreterError}; use super::value::Value; +mod unquote; + #[allow(unused)] pub struct Interpreter<'interner> { /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner - pub(super) interner: &'interner mut NodeInterner, + pub interner: &'interner mut NodeInterner, /// Each value currently in scope in the interpreter. /// Each element of the Vec represents a scope with every scope together making @@ -312,7 +317,7 @@ impl<'a> Interpreter<'a> { } /// Evaluate an expression and return the result - pub(super) fn evaluate(&mut self, id: ExprId) -> IResult { + pub fn evaluate(&mut self, id: ExprId) -> IResult { match self.interner.expression(&id) { HirExpression::Ident(ident, _) => self.evaluate_ident(ident, id), HirExpression::Literal(literal) => self.evaluate_literal(literal, id), @@ -328,7 +333,7 @@ impl<'a> Interpreter<'a> { HirExpression::If(if_) => self.evaluate_if(if_, id), HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), - HirExpression::Quote(block) => Ok(Value::Code(Rc::new(block))), + HirExpression::Quote(block) => self.evaluate_quote(block, id), HirExpression::Comptime(block) => self.evaluate_block(block), HirExpression::Unquote(block) => { // An Unquote expression being found is indicative of a macro being @@ -1120,6 +1125,15 @@ impl<'a> Interpreter<'a> { Ok(Value::Closure(lambda, environment, typ)) } + fn evaluate_quote(&mut self, mut quoted: HirQuoted, expr_id: ExprId) -> IResult { + let file = self.interner.expr_location(&expr_id).file; + let values = try_vecmap(quoted.unquoted_exprs, |value| self.evaluate(value))?; + let args = UnquoteArgs { values, file }; + + self.substitute_unquoted_values_into_block(&mut quoted.quoted_block, &args); + Ok(Value::Code(Rc::new(quoted.quoted_block))) + } + pub fn evaluate_statement(&mut self, statement: StmtId) -> IResult { match self.interner.statement(&statement) { HirStatement::Let(let_) => self.evaluate_let(let_), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs new file mode 100644 index 00000000000..58ca345ca90 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs @@ -0,0 +1,287 @@ +use fm::FileId; +use noirc_errors::Location; + +use crate::{ + ast::{ + ArrayLiteral, AssignStatement, ConstrainStatement, ConstructorExpression, IfExpression, + InfixExpression, Lambda, + }, + hir::comptime::{errors::IResult, Value}, + macros_api::{ + BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, + ForLoopStatement, ForRange, IndexExpression, LetStatement, Literal, MemberAccessExpression, + MethodCallExpression, PrefixExpression, Statement, StatementKind, + }, +}; + +use super::Interpreter; + +pub(super) struct UnquoteArgs { + pub(super) values: Vec, + pub(super) file: FileId, +} + +impl<'a> Interpreter<'a> { + pub(super) fn substitute_unquoted_values_into_block( + &mut self, + block: &mut BlockExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + for statement in &mut block.statements { + self.substitute_unquoted_into_statement(statement, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_statement( + &mut self, + statement: &mut Statement, + args: &UnquoteArgs, + ) -> IResult<()> { + match &mut statement.kind { + StatementKind::Let(let_) => self.substitute_unquoted_into_let(let_, args), + StatementKind::Constrain(constrain) => { + self.substitute_unquoted_into_constrain(constrain, args) + } + StatementKind::Expression(expr) => self.substitute_unquoted_into_expr(expr, args), + StatementKind::Assign(assign) => self.substitute_unquoted_into_assign(assign, args), + StatementKind::For(for_) => self.substitute_unquoted_into_for(for_, args), + StatementKind::Break => Ok(()), + StatementKind::Continue => Ok(()), + StatementKind::Comptime(comptime) => { + self.substitute_unquoted_into_statement(comptime, args) + } + StatementKind::Semi(expr) => self.substitute_unquoted_into_expr(expr, args), + StatementKind::Error => Ok(()), + } + } + + fn substitute_unquoted_into_constrain( + &mut self, + constrain: &mut ConstrainStatement, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut constrain.0, args)?; + if let Some(msg) = constrain.1.as_mut() { + self.substitute_unquoted_into_expr(msg, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_let( + &mut self, + let_: &mut LetStatement, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut let_.expression, args) + } + + fn substitute_unquoted_into_assign( + &mut self, + assign: &mut AssignStatement, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut assign.expression, args) + } + + fn substitute_unquoted_into_for( + &mut self, + for_: &mut ForLoopStatement, + args: &UnquoteArgs, + ) -> IResult<()> { + match &mut for_.range { + ForRange::Range(start, end) => { + self.substitute_unquoted_into_expr(start, args)?; + self.substitute_unquoted_into_expr(end, args)?; + } + ForRange::Array(array) => { + self.substitute_unquoted_into_expr(array, args)?; + } + }; + self.substitute_unquoted_into_expr(&mut for_.block, args) + } + + fn substitute_unquoted_into_expr( + &mut self, + expr: &mut Expression, + args: &UnquoteArgs, + ) -> IResult<()> { + match &mut expr.kind { + ExpressionKind::Literal(literal) => { + self.substitute_unquoted_into_literal(literal, args) + } + ExpressionKind::Block(block) => self.substitute_unquoted_values_into_block(block, args), + ExpressionKind::Prefix(prefix) => self.substitute_unquoted_into_prefix(prefix, args), + ExpressionKind::Index(index) => self.substitute_unquoted_into_index(index, args), + ExpressionKind::Call(call) => self.substitute_unquoted_into_call(call, args), + ExpressionKind::MethodCall(call) => { + self.substitute_unquoted_into_method_call(call, args) + } + ExpressionKind::Constructor(constructor) => { + self.substitute_unquoted_into_constructor(constructor, args) + } + ExpressionKind::MemberAccess(access) => { + self.substitute_unquoted_into_access(access, args) + } + ExpressionKind::Cast(cast) => self.substitute_unquoted_into_cast(cast, args), + ExpressionKind::Infix(infix) => self.substitute_unquoted_into_infix(infix, args), + ExpressionKind::If(if_) => self.substitute_unquoted_into_if(if_, args), + ExpressionKind::Variable(_, _) => Ok(()), + ExpressionKind::Tuple(tuple) => self.substitute_unquoted_into_tuple(tuple, args), + ExpressionKind::Lambda(lambda) => self.substitute_unquoted_into_lambda(lambda, args), + ExpressionKind::Parenthesized(expr) => self.substitute_unquoted_into_expr(expr, args), + ExpressionKind::Quote(quote, _) => { + self.substitute_unquoted_values_into_block(quote, args) + } + ExpressionKind::Unquote(unquote) => self.substitute_unquoted_into_expr(unquote, args), + ExpressionKind::Comptime(comptime, _) => { + self.substitute_unquoted_values_into_block(comptime, args) + } + ExpressionKind::Resolved(_) => Ok(()), + ExpressionKind::Error => Ok(()), + ExpressionKind::UnquoteMarker(index) => { + let location = Location::new(expr.span, args.file); + *expr = args.values[*index].clone().into_expression(self.interner, location)?; + Ok(()) + } + } + } + + fn substitute_unquoted_into_literal( + &mut self, + literal: &mut Literal, + args: &UnquoteArgs, + ) -> IResult<()> { + match literal { + Literal::Array(array) | Literal::Slice(array) => match array { + ArrayLiteral::Standard(elements) => { + for element in elements { + self.substitute_unquoted_into_expr(element, args)?; + } + Ok(()) + } + ArrayLiteral::Repeated { repeated_element, length } => { + self.substitute_unquoted_into_expr(repeated_element, args)?; + self.substitute_unquoted_into_expr(length, args)?; + Ok(()) + } + }, + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => Ok(()), + } + } + + fn substitute_unquoted_into_prefix( + &mut self, + prefix: &mut PrefixExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut prefix.rhs, args) + } + + fn substitute_unquoted_into_index( + &mut self, + index: &mut IndexExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut index.collection, args)?; + self.substitute_unquoted_into_expr(&mut index.index, args) + } + + fn substitute_unquoted_into_call( + &mut self, + call: &mut CallExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut call.func, args)?; + for arg in &mut call.arguments { + self.substitute_unquoted_into_expr(arg, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_method_call( + &mut self, + call: &mut MethodCallExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut call.object, args)?; + for arg in &mut call.arguments { + self.substitute_unquoted_into_expr(arg, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_constructor( + &mut self, + constructor: &mut ConstructorExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + for (_, field) in &mut constructor.fields { + self.substitute_unquoted_into_expr(field, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_access( + &mut self, + access: &mut MemberAccessExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut access.lhs, args) + } + + fn substitute_unquoted_into_cast( + &mut self, + cast: &mut CastExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut cast.lhs, args) + } + + fn substitute_unquoted_into_infix( + &mut self, + infix: &mut InfixExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut infix.lhs, args)?; + self.substitute_unquoted_into_expr(&mut infix.rhs, args) + } + + fn substitute_unquoted_into_if( + &mut self, + if_: &mut IfExpression, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut if_.condition, args)?; + self.substitute_unquoted_into_expr(&mut if_.consequence, args)?; + + if let Some(alternative) = if_.alternative.as_mut() { + self.substitute_unquoted_into_expr(alternative, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_tuple( + &mut self, + tuple: &mut [Expression], + args: &UnquoteArgs, + ) -> IResult<()> { + for field in tuple { + self.substitute_unquoted_into_expr(field, args)?; + } + Ok(()) + } + + fn substitute_unquoted_into_lambda( + &mut self, + lambda: &mut Lambda, + args: &UnquoteArgs, + ) -> IResult<()> { + self.substitute_unquoted_into_expr(&mut lambda.body, args) + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 11bbbc7484d..5d91b151218 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -70,7 +70,7 @@ impl Value { Value::Struct(_, typ) => return Cow::Borrowed(typ), Value::Array(_, typ) => return Cow::Borrowed(typ), Value::Slice(_, typ) => return Cow::Borrowed(typ), - Value::Code(_) => Type::Code, + Value::Code(_) => Type::Expr, Value::Pointer(element) => { let element = element.borrow().get_type().into_owned(); Type::MutableReference(Box::new(element)) diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 07fbde4db8e..2c6461cb77c 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -100,6 +100,12 @@ pub enum ResolverError { FoldAttributeOnUnconstrained { ident: Ident }, #[error("Invalid array length construction")] ArrayLengthInterpreter { error: InterpreterError }, + #[error("The unquote operator '$' can only be used within a quote expression")] + UnquoteUsedOutsideQuote { span: Span }, + #[error("Invalid syntax in macro call")] + InvalidSyntaxInMacroCall { span: Span }, + #[error("Macros must be comptime functions")] + MacroIsNotComptime { span: Span }, } impl ResolverError { @@ -391,6 +397,27 @@ impl<'a> From<&'a ResolverError> for Diagnostic { diag } ResolverError::ArrayLengthInterpreter { error } => Diagnostic::from(error), + ResolverError::UnquoteUsedOutsideQuote { span } => { + Diagnostic::simple_error( + "The unquote operator '$' can only be used within a quote expression".into(), + "".into(), + *span, + ) + }, + ResolverError::InvalidSyntaxInMacroCall { span } => { + Diagnostic::simple_error( + "Invalid syntax in macro call".into(), + "Macro calls must call a comptime function directly, they cannot use higher-order functions".into(), + *span, + ) + }, + ResolverError::MacroIsNotComptime{ span } => { + Diagnostic::simple_error( + "This macro call is to a non-comptime function".into(), + "Macro calls must be to comptime functions".into(), + *span, + ) + }, } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 133f971da19..4583125d2a2 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -17,7 +17,7 @@ use crate::hir_def::expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCapturedVar, HirCastExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess, - HirMethodCallExpression, HirPrefixExpression, ImplKind, + HirMethodCallExpression, HirPrefixExpression, HirQuoted, ImplKind, }; use crate::hir_def::function::FunctionBody; @@ -569,7 +569,7 @@ impl<'a> Resolver<'a> { let fields = self.resolve_type_inner(*fields); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } - Code => Type::Code, + Expr => Type::Expr, Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, @@ -1158,7 +1158,7 @@ impl<'a> Resolver<'a> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::Code + | Type::Expr | Type::Forall(_, _) => (), Type::TraitAsType(_, _, args) => { @@ -1641,13 +1641,23 @@ impl<'a> Resolver<'a> { ExpressionKind::Parenthesized(sub_expr) => return self.resolve_expression(*sub_expr), // The quoted expression isn't resolved since we don't want errors if variables aren't defined - ExpressionKind::Quote(block, _) => HirExpression::Quote(block), + ExpressionKind::Quote(block, _) => { + let quoted = HirQuoted { quoted_block: block, unquoted_exprs: Vec::new() }; + HirExpression::Quote(quoted) + } ExpressionKind::Comptime(block, _) => { HirExpression::Comptime(self.resolve_block(block)) } ExpressionKind::Resolved(_) => unreachable!( "ExpressionKind::Resolved should only be emitted by the comptime interpreter" ), + ExpressionKind::Unquote(_) => { + self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); + HirExpression::Literal(HirLiteral::Unit) + } + ExpressionKind::UnquoteMarker(index) => { + unreachable!("UnquoteMarker({index}) remaining in runtime code") + } }; // If these lines are ever changed, make sure to change the early return diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 0d8a9f3e717..bd32ba2fce5 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -143,6 +143,8 @@ pub enum TypeCheckError { }, #[error("Strings do not support indexed assignment")] StringIndexAssign { span: Span }, + #[error("Macro calls may only return Expr values")] + MacroReturningNonExpr { typ: Type, span: Span }, } impl TypeCheckError { @@ -335,6 +337,11 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { let msg = format!("Expected {expected_count} generic{expected_plural} from this function, but {actual_count} {actual_plural} provided"); Diagnostic::simple_error(msg, "".into(), *span) }, + TypeCheckError::MacroReturningNonExpr { typ, span } => Diagnostic::simple_error( + format!("Expected macro call to return an `Expr` but found a(n) {typ}"), + "Macro calls must return quoted expressions, otherwise there is no code to insert".into(), + *span, + ), } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 50af9dbf34e..503bbbf79be 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -307,7 +307,7 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } - HirExpression::Quote(_) => Type::Code, + HirExpression::Quote(_) => Type::Expr, HirExpression::Comptime(block) => self.check_block(block), // Unquote should be inserted & removed by the comptime interpreter. diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 163ea10ee02..9547aef866b 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -33,7 +33,7 @@ pub enum HirExpression { If(HirIfExpression), Tuple(Vec), Lambda(HirLambda), - Quote(crate::ast::BlockExpression), + Quote(HirQuoted), Unquote(crate::ast::BlockExpression), Comptime(HirBlockExpression), Error, @@ -291,3 +291,13 @@ pub struct HirLambda { pub body: ExprId, pub captures: Vec, } + +#[derive(Debug, Clone)] +pub struct HirQuoted { + pub quoted_block: crate::ast::BlockExpression, + + /// Each expression here corresponds to a `ExpressionKind::UnquoteMarker(index)` in `quoted_block`. + /// The values of these expressions after evaluation will be inlined into the position + /// indicated by their corresponding UnquoteMarker with that index. + pub unquoted_exprs: Vec, +} diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ff8b11f3916..f5c90b33c11 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -105,7 +105,7 @@ pub enum Type { Constant(u32), /// The type of quoted code in macros. This is always a comptime-only type - Code, + Expr, /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with @@ -147,7 +147,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) - | Type::Code + | Type::Expr | Type::Slice(_) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } @@ -640,7 +640,7 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::Forall(_, _) - | Type::Code => false, + | Type::Expr => false, Type::TraitAsType(_, _, args) => { args.iter().any(|generic| generic.contains_numeric_typevar(target_id)) @@ -706,7 +706,7 @@ impl Type { | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) - | Type::Code + | Type::Expr | Type::Slice(_) | Type::TraitAsType(..) => false, @@ -755,7 +755,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) // TODO: probably can allow code as it is all compile time - | Type::Code + | Type::Expr | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { @@ -918,7 +918,7 @@ impl std::fmt::Display for Type { Type::MutableReference(element) => { write!(f, "&mut {element}") } - Type::Code => write!(f, "Code"), + Type::Expr => write!(f, "Expr"), } } } @@ -1639,7 +1639,7 @@ impl Type { | Type::Bool | Type::Constant(_) | Type::Error - | Type::Code + | Type::Expr | Type::Unit => self.clone(), } } @@ -1682,7 +1682,7 @@ impl Type { | Type::Bool | Type::Constant(_) | Type::Error - | Type::Code + | Type::Expr | Type::Unit => false, } } @@ -1739,7 +1739,7 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Code | Error => self.clone(), + FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Expr | Error => self.clone(), } } @@ -1874,7 +1874,7 @@ impl From<&Type> for PrintableType { Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } - Type::Code => unreachable!(), + Type::Expr => unreachable!(), } } } @@ -1959,7 +1959,7 @@ impl std::fmt::Debug for Type { Type::MutableReference(element) => { write!(f, "&mut {element:?}") } - Type::Code => write!(f, "Code"), + Type::Expr => write!(f, "Expr"), } } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 7ecea5c9eac..c6cc1344aae 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1012,7 +1012,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } - HirType::Code => unreachable!("Tried to translate Code type into runtime code"), + HirType::Expr => unreachable!("Tried to translate Code type into runtime code"), }) } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index e29c3183993..5fdce80087a 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1825,7 +1825,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), Type::NamedGeneric(_, _) => Some(Generic), - Type::Code => Some(Code), + Type::Expr => Some(Code), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 5107e10f4df..0d8215f2ea4 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1123,8 +1123,7 @@ where P: ExprParser + 'a, { let unquote = variable().map_with_span(Expression::new).or(parenthesized(expr_parser)); - // This will be updated to ExpressionKind::Unquote in a later PR - just(Token::DollarSign).ignore_then(unquote).map(|_| ExpressionKind::Error) + just(Token::DollarSign).ignore_then(unquote).map(|expr| ExpressionKind::Unquote(Box::new(expr))) } fn tuple

(expr_parser: P) -> impl NoirParser @@ -1548,27 +1547,19 @@ mod test { Case { source: "let = 4 + 3", expect: "let $error: unspecified = (4 + 3)", errors: 1 }, Case { source: "let = ", expect: "let $error: unspecified = Error", errors: 2 }, Case { source: "let", expect: "let $error: unspecified = Error", errors: 3 }, - Case { source: "foo = one two three", expect: "foo = plain::one", errors: 1 }, + Case { source: "foo = one two three", expect: "foo = one", errors: 1 }, Case { source: "constrain", expect: "constrain Error", errors: 2 }, Case { source: "assert", expect: "constrain Error", errors: 1 }, - Case { source: "constrain x ==", expect: "constrain (plain::x == Error)", errors: 2 }, - Case { source: "assert(x ==)", expect: "constrain (plain::x == Error)", errors: 1 }, - Case { - source: "assert(x == x, x)", - expect: "constrain (plain::x == plain::x)", - errors: 0, - }, + Case { source: "constrain x ==", expect: "constrain (x == Error)", errors: 2 }, + Case { source: "assert(x ==)", expect: "constrain (x == Error)", errors: 1 }, + Case { source: "assert(x == x, x)", expect: "constrain (x == x)", errors: 0 }, Case { source: "assert_eq(x,)", expect: "constrain (Error == Error)", errors: 1 }, Case { source: "assert_eq(x, x, x, x)", expect: "constrain (Error == Error)", errors: 1, }, - Case { - source: "assert_eq(x, x, x)", - expect: "constrain (plain::x == plain::x)", - errors: 0, - }, + Case { source: "assert_eq(x, x, x)", expect: "constrain (x == x)", errors: 0 }, ]; check_cases_with_errors(&cases[..], fresh_statement()); @@ -1610,7 +1601,7 @@ mod test { source: "{ if structure { a: 1 } {} }", expect: concat!( "{\n", - " if plain::structure {\n", + " if structure {\n", " Error\n", " }\n", " {\n", @@ -1621,22 +1612,17 @@ mod test { }, Case { source: "{ if ( structure { a: 1 } ) {} }", - expect: concat!("{\n", " if ((plain::structure { a: 1 })) {\n", " }\n", "}",), + expect: concat!("{\n", " if ((structure { a: 1 })) {\n", " }\n", "}",), errors: 0, }, Case { source: "{ if ( structure {} ) {} }", - expect: concat!("{\n", " if ((plain::structure { })) {\n", " }\n", "}"), + expect: concat!("{\n", " if ((structure { })) {\n", " }\n", "}"), errors: 0, }, Case { source: "{ if (a { x: 1 }, b { y: 2 }) {} }", - expect: concat!( - "{\n", - " if ((plain::a { x: 1 }), (plain::b { y: 2 })) {\n", - " }\n", - "}", - ), + expect: concat!("{\n", " if ((a { x: 1 }), (b { y: 2 })) {\n", " }\n", "}",), errors: 0, }, Case { @@ -1644,8 +1630,8 @@ mod test { expect: concat!( "{\n", " if ({\n", - " let foo: unspecified = (plain::bar { baz: 42 })\n", - " (plain::foo == (plain::bar { baz: 42 }))\n", + " let foo: unspecified = (bar { baz: 42 })\n", + " (foo == (bar { baz: 42 }))\n", " }) {\n", " }\n", "}", diff --git a/compiler/noirc_frontend/src/parser/parser/literals.rs b/compiler/noirc_frontend/src/parser/parser/literals.rs index 584224fda46..b25b6acc9e2 100644 --- a/compiler/noirc_frontend/src/parser/parser/literals.rs +++ b/compiler/noirc_frontend/src/parser/parser/literals.rs @@ -108,18 +108,18 @@ mod test { #[allow(clippy::needless_raw_string_hashes)] Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, // miscellaneous - Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 }, - Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 }, + Case { source: r##" r#\"foo\"# "##, expect: "r", errors: 2 }, + Case { source: r#" r\"foo\" "#, expect: "r", errors: 1 }, Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, // missing 'r' letter Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 }, - Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 }, + Case { source: r#" #"foo" "#, expect: "foo", errors: 2 }, // whitespace - Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 }, - Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 }, + Case { source: r##" r #"foo"# "##, expect: "r", errors: 2 }, + Case { source: r##" r# "foo"# "##, expect: "r", errors: 3 }, Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 }, // after identifier - Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 }, + Case { source: r##" bar#"foo"# "##, expect: "bar", errors: 2 }, // nested Case { source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index a79a2ef67f2..40edc9936d1 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -69,7 +69,7 @@ pub(super) fn bool_type() -> impl NoirParser { /// This is the type `Expr` - the type of a quoted, untyped expression object used for macros pub(super) fn expr_type() -> impl NoirParser { - keyword(Keyword::Expr).map_with_span(|_, span| UnresolvedTypeData::Code.with_span(span)) + keyword(Keyword::Expr).map_with_span(|_, span| UnresolvedTypeData::Expr.with_span(span)) } pub(super) fn string_type() -> impl NoirParser { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 99215c8f173..1acf1fbf3f2 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -619,7 +619,7 @@ fn check_impl_struct_not_trait() { CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { not_a_trait_name, }) => { - assert_eq!(not_a_trait_name.to_string(), "plain::Default"); + assert_eq!(not_a_trait_name.to_string(), "Default"); } _ => { panic!("No other errors are expected! Found = {:?}", err); diff --git a/test_programs/compile_success_empty/macros/Nargo.toml b/test_programs/compile_success_empty/macros/Nargo.toml new file mode 100644 index 00000000000..e4408e2ee17 --- /dev/null +++ b/test_programs/compile_success_empty/macros/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "macros" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/macros/src/main.nr b/test_programs/compile_success_empty/macros/src/main.nr new file mode 100644 index 00000000000..f466226d575 --- /dev/null +++ b/test_programs/compile_success_empty/macros/src/main.nr @@ -0,0 +1,15 @@ +comptime fn my_macro(x: Field, y: Field) -> Expr { + // Current version of macros in Noir are not hygienic + // so we can quote a and b here and expect them to resolve + // to the a and b in main at the callsite of my_macro. + quote { + $x + $y + a + b + } +} + +fn main() { + let a = 100; + let b = 200; + let result = my_macro!(1, 2); + assert_eq(result, 1 + 2 + a + b); +} diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index a9193896589..a3971d437fb 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -11,7 +11,8 @@ fold_distinct_return fold_fibonacci fold_numeric_generic_poseidon is_unconstrained +macros modulus references regression_4709 -to_bytes_integration \ No newline at end of file +to_bytes_integration diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index fcc09653c7d..846160320cf 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -42,22 +42,27 @@ fn main() { /// These should be fixed and removed from this list. const IGNORED_BRILLIG_TESTS: [&str; 11] = [ // Takes a very long time to execute as large loops do not get simplified. - &"regression_4709", + "regression_4709", // bit sizes for bigint operation doesn't match up. - &"bigint", + "bigint", // ICE due to looking for function which doesn't exist. - &"fold_after_inlined_calls", - &"fold_basic", - &"fold_basic_nested_call", - &"fold_call_witness_condition", - &"fold_complex_outputs", - &"fold_distinct_return", - &"fold_fibonacci", - &"fold_numeric_generic_poseidon", + "fold_after_inlined_calls", + "fold_basic", + "fold_basic_nested_call", + "fold_call_witness_condition", + "fold_complex_outputs", + "fold_distinct_return", + "fold_fibonacci", + "fold_numeric_generic_poseidon", // Expected to fail as test asserts on which runtime it is in. - &"is_unconstrained", + "is_unconstrained", ]; +/// Certain comptime features are only available in the elaborator. +/// We skip these tests for non-elaborator code since they are not +/// expected to work there. This can be removed once the old code is removed. +const IGNORED_COMPTIME_TESTS: [&str; 1] = ["macros"]; + fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; let test_data_dir = test_data_dir.join(test_sub_dir); @@ -281,10 +286,13 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa }; let test_dir = &test_dir.path(); + let comptime_ignored = + if IGNORED_COMPTIME_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" }; + write!( test_file, r#" -#[test] +#[test]{comptime_ignored} fn compile_success_empty_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 1d450bb7e28..ec8ac4abec7 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -178,6 +178,14 @@ pub(crate) fn rewrite( ExpressionKind::Resolved(_) => { unreachable!("ExpressionKind::Resolved should only emitted by the comptime interpreter") } + ExpressionKind::Unquote(expr) => { + if matches!(&expr.kind, ExpressionKind::Variable(..)) { + format!("${expr}") + } else { + format!("$({})", rewrite_sub_expr(visitor, shape, *expr)) + } + } + ExpressionKind::UnquoteMarker(_) => unreachable!("UnquoteMarker in runtime code"), } } diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index 278457f82d1..3ad1d9302be 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -64,7 +64,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) | UnresolvedTypeData::Expression(_) | UnresolvedTypeData::String(_) | UnresolvedTypeData::FormatString(_, _) - | UnresolvedTypeData::Code + | UnresolvedTypeData::Expr | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span.unwrap()).into(), UnresolvedTypeData::Error => unreachable!(), } From b351cc07be4ce7e46314ce378f4bef6776b2d4f3 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:58:34 +0200 Subject: [PATCH 33/57] chore: add no-predicate to hash implementations (#5253) # Description ## Problem\* Related to #4688 ## Summary\* Adding the no-predicate attribute to the hash implementations of the stdlib ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/hash/mimc.nr | 1 + noir_stdlib/src/hash/poseidon/bn254.nr | 17 ++++++++++++++ noir_stdlib/src/sha256.nr | 1 + noir_stdlib/src/sha512.nr | 1 + .../regression_5252/Nargo.toml | 7 ++++++ .../regression_5252/Prover.toml | 6 +++++ .../regression_5252/src/main.nr | 23 +++++++++++++++++++ 7 files changed, 56 insertions(+) create mode 100644 test_programs/execution_success/regression_5252/Nargo.toml create mode 100644 test_programs/execution_success/regression_5252/Prover.toml create mode 100644 test_programs/execution_success/regression_5252/src/main.nr diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr index 6c5502c2fbf..e90bacb75c2 100644 --- a/noir_stdlib/src/hash/mimc.nr +++ b/noir_stdlib/src/hash/mimc.nr @@ -116,6 +116,7 @@ global MIMC_BN254_CONSTANTS: [Field; MIMC_BN254_ROUNDS] = [ //mimc implementation with hardcoded parameters for BN254 curve. #[field(bn254)] +#[no_predicates] pub fn mimc_bn254(array: [Field; N]) -> Field { let exponent = 7; let mut r = 0; diff --git a/noir_stdlib/src/hash/poseidon/bn254.nr b/noir_stdlib/src/hash/poseidon/bn254.nr index 54f22884e29..9d3accb1ebd 100644 --- a/noir_stdlib/src/hash/poseidon/bn254.nr +++ b/noir_stdlib/src/hash/poseidon/bn254.nr @@ -6,12 +6,14 @@ use crate::hash::poseidon::{PoseidonConfig, absorb}; // Variable-length Poseidon-128 sponge as suggested in second bullet point of §3 of https://eprint.iacr.org/2019/458.pdf #[field(bn254)] +#[no_predicates] pub fn sponge(msg: [Field; N]) -> Field { absorb(consts::x5_5_config(), [0; 5], 4, 1, msg)[1] } // Various instances of the Poseidon hash function // Consistent with Circom's implementation +#[no_predicates] pub fn hash_1(input: [Field; 1]) -> Field { let mut state = [0; 2]; for i in 0..input.len() { @@ -21,6 +23,7 @@ pub fn hash_1(input: [Field; 1]) -> Field { perm::x5_2(state)[0] } +#[no_predicates] pub fn hash_2(input: [Field; 2]) -> Field { let mut state = [0; 3]; for i in 0..input.len() { @@ -30,6 +33,7 @@ pub fn hash_2(input: [Field; 2]) -> Field { perm::x5_3(state)[0] } +#[no_predicates] pub fn hash_3(input: [Field; 3]) -> Field { let mut state = [0; 4]; for i in 0..input.len() { @@ -39,6 +43,7 @@ pub fn hash_3(input: [Field; 3]) -> Field { perm::x5_4(state)[0] } +#[no_predicates] pub fn hash_4(input: [Field; 4]) -> Field { let mut state = [0; 5]; for i in 0..input.len() { @@ -48,6 +53,7 @@ pub fn hash_4(input: [Field; 4]) -> Field { perm::x5_5(state)[0] } +#[no_predicates] pub fn hash_5(input: [Field; 5]) -> Field { let mut state = [0; 6]; for i in 0..input.len() { @@ -57,6 +63,7 @@ pub fn hash_5(input: [Field; 5]) -> Field { perm::x5_6(state)[0] } +#[no_predicates] pub fn hash_6(input: [Field; 6]) -> Field { let mut state = [0; 7]; for i in 0..input.len() { @@ -66,6 +73,7 @@ pub fn hash_6(input: [Field; 6]) -> Field { perm::x5_7(state)[0] } +#[no_predicates] pub fn hash_7(input: [Field; 7]) -> Field { let mut state = [0; 8]; for i in 0..input.len() { @@ -75,6 +83,7 @@ pub fn hash_7(input: [Field; 7]) -> Field { perm::x5_8(state)[0] } +#[no_predicates] pub fn hash_8(input: [Field; 8]) -> Field { let mut state = [0; 9]; for i in 0..input.len() { @@ -84,6 +93,7 @@ pub fn hash_8(input: [Field; 8]) -> Field { perm::x5_9(state)[0] } +#[no_predicates] pub fn hash_9(input: [Field; 9]) -> Field { let mut state = [0; 10]; for i in 0..input.len() { @@ -93,6 +103,7 @@ pub fn hash_9(input: [Field; 9]) -> Field { perm::x5_10(state)[0] } +#[no_predicates] pub fn hash_10(input: [Field; 10]) -> Field { let mut state = [0; 11]; for i in 0..input.len() { @@ -102,6 +113,7 @@ pub fn hash_10(input: [Field; 10]) -> Field { perm::x5_11(state)[0] } +#[no_predicates] pub fn hash_11(input: [Field; 11]) -> Field { let mut state = [0; 12]; for i in 0..input.len() { @@ -111,6 +123,7 @@ pub fn hash_11(input: [Field; 11]) -> Field { perm::x5_12(state)[0] } +#[no_predicates] pub fn hash_12(input: [Field; 12]) -> Field { let mut state = [0; 13]; for i in 0..input.len() { @@ -120,6 +133,7 @@ pub fn hash_12(input: [Field; 12]) -> Field { perm::x5_13(state)[0] } +#[no_predicates] pub fn hash_13(input: [Field; 13]) -> Field { let mut state = [0; 14]; for i in 0..input.len() { @@ -129,6 +143,7 @@ pub fn hash_13(input: [Field; 13]) -> Field { perm::x5_14(state)[0] } +#[no_predicates] pub fn hash_14(input: [Field; 14]) -> Field { let mut state = [0; 15]; for i in 0..input.len() { @@ -138,6 +153,7 @@ pub fn hash_14(input: [Field; 14]) -> Field { perm::x5_15(state)[0] } +#[no_predicates] pub fn hash_15(input: [Field; 15]) -> Field { let mut state = [0; 16]; for i in 0..input.len() { @@ -147,6 +163,7 @@ pub fn hash_15(input: [Field; 15]) -> Field { perm::x5_16(state)[0] } +#[no_predicates] pub fn hash_16(input: [Field; 16]) -> Field { let mut state = [0; 17]; for i in 0..input.len() { diff --git a/noir_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index d856043fcfa..6057876951d 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -38,6 +38,7 @@ fn hash_final_block(msg_block: [u8; 64], mut state: [u32; 8]) -> [u8; 32] { } // Variable size SHA-256 hash +#[no_predicates] pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { let mut msg_block: [u8; 64] = [0; 64]; let mut h: [u32; 8] = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]; // Intermediate hash, starting with the canonical initial value diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index 0f8ffcfcb1c..4e46840ebb7 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -87,6 +87,7 @@ fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] { msg64 } // SHA-512 hash function +#[no_predicates] pub fn digest(msg: [u8; N]) -> [u8; 64] { let mut msg_block: [u8; 128] = [0; 128]; // noir-fmt:ignore diff --git a/test_programs/execution_success/regression_5252/Nargo.toml b/test_programs/execution_success/regression_5252/Nargo.toml new file mode 100644 index 00000000000..855507dfaf3 --- /dev/null +++ b/test_programs/execution_success/regression_5252/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_5252" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/regression_5252/Prover.toml b/test_programs/execution_success/regression_5252/Prover.toml new file mode 100644 index 00000000000..82776b4463d --- /dev/null +++ b/test_programs/execution_success/regression_5252/Prover.toml @@ -0,0 +1,6 @@ +to_hash = [[1,5,9,2,24,563,3545,5,52,4244,43,2,7373567,2,286762,7,2457,24,2456,2456], +[2234,2,26,27,24566,132452,3452456344567,657,45674657,4567467,45674,4567456,4567,23454,2345,2345345245,25252345,2435234524366,8678678,67867567], +[9887575467567,5367367243617,46244567783,64673425,67456573456,4673457,46735,745674,6574,567456,7456,84,683,683,8368,38,32,16,7,98], +#[465656,234324,4353,5245246,2567345674567,5634563456,7676474,4747,4567456746,56,4657456,4657,4567,46,7,8,98,87,76,57] +] +enable = [1,1,0] diff --git a/test_programs/execution_success/regression_5252/src/main.nr b/test_programs/execution_success/regression_5252/src/main.nr new file mode 100644 index 00000000000..315807c3396 --- /dev/null +++ b/test_programs/execution_success/regression_5252/src/main.nr @@ -0,0 +1,23 @@ +use dep::std::hash::{mimc, poseidon, poseidon2::Poseidon2}; + +global NUM_HASHES = 3; +global HASH_LENGTH = 20; + +pub fn poseidon_hash(inputs: [Field; N]) -> Field { + Poseidon2::hash(inputs, inputs.len()) +} + +fn main( + to_hash: [[Field; HASH_LENGTH]; NUM_HASHES], + enable: [bool; NUM_HASHES] +) -> pub [Field; NUM_HASHES] { + let mut result = [0; NUM_HASHES]; + for i in 0..NUM_HASHES { + let enable = enable[i]; + let to_hash = to_hash[i]; + if enable { + result[i] = poseidon_hash(to_hash) + poseidon::bn254::sponge(to_hash) + mimc::mimc_bn254(to_hash); + } + } + result +} From c08799de12a7881f0a228f913ea0cbf15f74f2f1 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:58:20 +0100 Subject: [PATCH 34/57] chore: replace `is_bn254` implementation to not rely on truncation of literals (#5247) # Description ## Problem\* Resolves ## Summary\* This PR updates the `is_bn254` function so we don't rely on the footgun-y behaviour being reported in #4631 in order to determine the field choice ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/compat.nr | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/noir_stdlib/src/compat.nr b/noir_stdlib/src/compat.nr index 5d80c422c33..30b7f73f130 100644 --- a/noir_stdlib/src/compat.nr +++ b/noir_stdlib/src/compat.nr @@ -1,4 +1,18 @@ +global BN254_MODULUS_BE_BYTES: [u8; 32] = [ + 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 +]; + pub fn is_bn254() -> bool { - // bn254 truncates its curve order to 0 - 21888242871839275222246405745257275088548364400416034343698204186575808495617 == 0 + // TODO: refactor this once https://github.com/noir-lang/noir/issues/5245 is resolved. + let modulus_bytes = crate::field::modulus_be_bytes(); + if modulus_bytes.len() == 32 { + let mut modulus_bytes_array: [u8; 32] = [0; 32]; + for i in 0..32 { + modulus_bytes_array[i] = modulus_bytes[i]; + } + + modulus_bytes_array == BN254_MODULUS_BE_BYTES + } else { + false + } } From 7de19f5856591203271836f07154abae13f5102b Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:31:28 -0400 Subject: [PATCH 35/57] feat: Sync from aztec-packages (#5242) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6986) feat: `pxe.addNullifiedNote(...)` (https://github.com/AztecProtocol/aztec-packages/pull/6948) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Maxim Vezenov --- .aztec-sync-commit | 2 +- aztec_macros/src/lib.rs | 4 +- ...e_note_hash_and_optionally_a_nullifier.rs} | 54 ++++++++++--------- aztec_macros/src/transforms/mod.rs | 2 +- aztec_macros/src/utils/errors.rs | 6 +-- compiler/integration-tests/package.json | 2 +- .../test/node/prove_and_verify.test.ts | 36 ++++++------- .../src/hir/comptime/interpreter.rs | 15 ++++-- .../noirc_frontend/src/hir/comptime/scan.rs | 1 - compiler/noirc_frontend/src/hir_def/types.rs | 4 ++ .../benchmarks/bench_sha256/Nargo.toml | 7 --- .../noirc_abi_wasm/test/shared/abi_encode.ts | 2 - .../test/shared/array_as_field.ts | 2 - .../test/shared/decode_error.ts | 2 - .../test/shared/field_as_array.ts | 2 - tooling/noirc_abi_wasm/test/shared/structs.ts | 6 --- .../test/shared/uint_overflow.ts | 2 - 17 files changed, 67 insertions(+), 82 deletions(-) rename aztec_macros/src/transforms/{compute_note_hash_and_nullifier.rs => compute_note_hash_and_optionally_a_nullifier.rs} (80%) delete mode 100644 test_programs/benchmarks/bench_sha256/Nargo.toml diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 0bdea76121d..bdaabc69727 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -86a33140f9a65e518003b3f4c60f97d132f85b89 +12af650f0d27c37dca06bb329bf76a5574534d78 diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 2daf86dc643..a36b7b17d09 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -3,7 +3,7 @@ mod utils; use noirc_errors::Location; use transforms::{ - compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, + compute_note_hash_and_optionally_a_nullifier::inject_compute_note_hash_and_optionally_a_nullifier, contract_interface::{ generate_contract_interface, stub_function, update_fn_signatures_in_contract_interface, }, @@ -236,7 +236,7 @@ fn transform_hir( ) -> Result<(), (AztecMacroError, FileId)> { if has_aztec_dependency(crate_id, context) { transform_events(crate_id, context)?; - inject_compute_note_hash_and_nullifier(crate_id, context)?; + inject_compute_note_hash_and_optionally_a_nullifier(crate_id, context)?; assign_storage_slots(crate_id, context)?; inject_note_exports(crate_id, context)?; update_fn_signatures_in_contract_interface(crate_id, context) diff --git a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs similarity index 80% rename from aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs rename to aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs index f624cde9969..30c0f63a2d4 100644 --- a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs +++ b/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs @@ -14,16 +14,16 @@ use crate::utils::{ }, }; -// Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined -fn check_for_compute_note_hash_and_nullifier_definition( +// Check if "compute_note_hash_and_optionally_a_nullifier(AztecAddress,Field,Field,Field,bool,[Field; N]) -> [Field; 4]" is defined +fn check_for_compute_note_hash_and_optionally_a_nullifier_definition( crate_id: &CrateId, context: &HirContext, ) -> bool { collect_crate_functions(crate_id, context).iter().any(|funct_id| { let func_data = context.def_interner.function_meta(funct_id); let func_name = context.def_interner.function_name(funct_id); - func_name == "compute_note_hash_and_nullifier" - && func_data.parameters.len() == 5 + func_name == "compute_note_hash_and_optionally_a_nullifier" + && func_data.parameters.len() == 6 && func_data.parameters.0.first().is_some_and(| (_, typ, _) | match typ { Type::Struct(struct_typ, _) => struct_typ.borrow().name.0.contents == "AztecAddress", _ => false @@ -31,8 +31,9 @@ fn check_for_compute_note_hash_and_nullifier_definition( && func_data.parameters.0.get(1).is_some_and(|(_, typ, _)| typ.is_field()) && func_data.parameters.0.get(2).is_some_and(|(_, typ, _)| typ.is_field()) && func_data.parameters.0.get(3).is_some_and(|(_, typ, _)| typ.is_field()) - // checks if the 5th parameter is an array and contains only fields - && func_data.parameters.0.get(4).is_some_and(|(_, typ, _)| match typ { + && func_data.parameters.0.get(4).is_some_and(|(_, typ, _)| typ.is_bool()) + // checks if the 6th parameter is an array and contains only fields + && func_data.parameters.0.get(5).is_some_and(|(_, typ, _)| match typ { Type::Array(_, inner_type) => inner_type.to_owned().is_field(), _ => false }) @@ -49,16 +50,16 @@ fn check_for_compute_note_hash_and_nullifier_definition( }) } -pub fn inject_compute_note_hash_and_nullifier( +pub fn inject_compute_note_hash_and_optionally_a_nullifier( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { if let Some((_, module_id, file_id)) = get_contract_module_data(context, crate_id) { - // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an + // If compute_note_hash_and_optionally_a_nullifier is already defined by the user, we skip auto-generation in order to provide an // escape hatch for this mechanism. // TODO(#4647): improve this diagnosis and error messaging. if context.crate_graph.root_crate_id() != crate_id - || check_for_compute_note_hash_and_nullifier_definition(crate_id, context) + || check_for_compute_note_hash_and_optionally_a_nullifier_definition(crate_id, context) { return Ok(()); } @@ -69,14 +70,14 @@ pub fn inject_compute_note_hash_and_nullifier( let max_note_length_const = get_global_numberic_const(context, "MAX_NOTE_FIELDS_LENGTH") .map_err(|err| { ( - AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { + AztecMacroError::CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message: Some(err.primary_message), }, file_id, ) })?; - // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the + // In order to implement compute_note_hash_and_optionally_a_nullifier, we need to know all of the different note types the // contract might use and their serialized lengths. These are the types that are marked as #[aztec(note)]. let mut notes_and_lengths = vec![]; @@ -89,7 +90,7 @@ pub fn inject_compute_note_hash_and_nullifier( ) .map_err(|_err| { ( - AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { + AztecMacroError::CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message: Some(format!( "Failed to get serialized length for note type {}", path @@ -102,7 +103,7 @@ pub fn inject_compute_note_hash_and_nullifier( if serialized_len > max_note_length_const { return Err(( - AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { + AztecMacroError::CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message: Some(format!( "Note type {} as {} fields, which is more than the maximum allowed length of {}.", path, @@ -120,11 +121,12 @@ pub fn inject_compute_note_hash_and_nullifier( let max_note_length: u128 = *notes_and_lengths.iter().map(|(_, serialized_len)| serialized_len).max().unwrap_or(&0); - let note_types = + let note_types: Vec = notes_and_lengths.iter().map(|(note_type, _)| note_type.clone()).collect::>(); - // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. - let func = generate_compute_note_hash_and_nullifier(¬e_types, max_note_length); + // We can now generate a version of compute_note_hash_and_optionally_a_nullifier tailored for the contract in this crate. + let func = + generate_compute_note_hash_and_optionally_a_nullifier(¬e_types, max_note_length); // And inject the newly created function into the contract. @@ -134,7 +136,7 @@ pub fn inject_compute_note_hash_and_nullifier( inject_fn(crate_id, context, func, location, module_id, file_id).map_err(|err| { ( - AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { + AztecMacroError::CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message: err.secondary_message, }, file_id, @@ -144,12 +146,12 @@ pub fn inject_compute_note_hash_and_nullifier( Ok(()) } -fn generate_compute_note_hash_and_nullifier( +fn generate_compute_note_hash_and_optionally_a_nullifier( note_types: &[String], max_note_length: u128, ) -> NoirFunction { let function_source = - generate_compute_note_hash_and_nullifier_source(note_types, max_note_length); + generate_compute_note_hash_and_optionally_a_nullifier_source(note_types, max_note_length); let (function_ast, errors) = parse_program(&function_source); if !errors.is_empty() { @@ -161,7 +163,7 @@ fn generate_compute_note_hash_and_nullifier( function_ast.functions.remove(0) } -fn generate_compute_note_hash_and_nullifier_source( +fn generate_compute_note_hash_and_optionally_a_nullifier_source( note_types: &[String], max_note_length: u128, ) -> String { @@ -173,12 +175,13 @@ fn generate_compute_note_hash_and_nullifier_source( // so we include a dummy version. format!( " - unconstrained fn compute_note_hash_and_nullifier( + unconstrained fn compute_note_hash_and_optionally_a_nullifier( contract_address: dep::aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, - serialized_note: [Field; {}] + compute_nullifier: bool, + serialized_note: [Field; {}], ) -> pub [Field; 4] {{ assert(false, \"This contract does not use private notes\"); [0, 0, 0, 0] @@ -191,7 +194,7 @@ fn generate_compute_note_hash_and_nullifier_source( let if_statements: Vec = note_types.iter().map(|note_type| format!( "if (note_type_id == {0}::get_note_type_id()) {{ - dep::aztec::note::utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) + dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier({0}::deserialize_content, note_header, compute_nullifier, serialized_note) }}" , note_type)).collect(); @@ -204,12 +207,13 @@ fn generate_compute_note_hash_and_nullifier_source( format!( " - unconstrained fn compute_note_hash_and_nullifier( + unconstrained fn compute_note_hash_and_optionally_a_nullifier( contract_address: dep::aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, - serialized_note: [Field; {}] + compute_nullifier: bool, + serialized_note: [Field; {}], ) -> pub [Field; 4] {{ let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/aztec_macros/src/transforms/mod.rs b/aztec_macros/src/transforms/mod.rs index 2a6fef7647f..bd419bced6f 100644 --- a/aztec_macros/src/transforms/mod.rs +++ b/aztec_macros/src/transforms/mod.rs @@ -1,4 +1,4 @@ -pub mod compute_note_hash_and_nullifier; +pub mod compute_note_hash_and_optionally_a_nullifier; pub mod contract_interface; pub mod events; pub mod functions; diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 51aea3d052f..852b5f1e57a 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -12,7 +12,7 @@ pub enum AztecMacroError { UnsupportedFunctionReturnType { span: Span, typ: ast::UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: ast::UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, - CouldNotImplementComputeNoteHashAndNullifier { secondary_message: Option }, + CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, MultipleStorageDefinitions { span: Option }, CouldNotExportStorageLayout { span: Option, secondary_message: Option }, @@ -57,8 +57,8 @@ impl From for MacroError { secondary_message, span: None, }, - AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { secondary_message } => MacroError { - primary_message: "Could not implement compute_note_hash_and_nullifier automatically, please provide an implementation".to_string(), + AztecMacroError::CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message } => MacroError { + primary_message: "Could not implement compute_note_hash_and_optionally_a_nullifier automatically, please provide an implementation".to_string(), secondary_message, span: None, }, diff --git a/compiler/integration-tests/package.json b/compiler/integration-tests/package.json index b789f1f05ce..0638fffe547 100644 --- a/compiler/integration-tests/package.json +++ b/compiler/integration-tests/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "echo Integration Test build step", "test": "yarn test:browser && yarn test:node", - "test:node": "bash ./scripts/setup.sh && hardhat test test/node/**/*", + "test:node": "bash ./scripts/setup.sh && hardhat test test/node/prove_and_verify.test.ts && hardhat test test/node/smart_contract_verifier.test.ts && hardhat test test/node/onchain_recursive_verification.test.ts", "test:browser": "web-test-runner", "test:integration:browser": "web-test-runner test/browser/**/*.test.ts", "test:integration:browser:watch": "web-test-runner test/browser/**/*.test.ts --watch", diff --git a/compiler/integration-tests/test/node/prove_and_verify.test.ts b/compiler/integration-tests/test/node/prove_and_verify.test.ts index 15d39436a3b..699dcf5e918 100644 --- a/compiler/integration-tests/test/node/prove_and_verify.test.ts +++ b/compiler/integration-tests/test/node/prove_and_verify.test.ts @@ -8,6 +8,8 @@ import { CompiledCircuit } from '@noir-lang/types'; const assert_lt_program = assert_lt_json as CompiledCircuit; const fold_fibonacci_program = fold_fibonacci_json as CompiledCircuit; +const backend = new Backend(assert_lt_program); + it('end-to-end proof creation and verification (outer)', async () => { // Noir.Js part const inputs = { @@ -22,11 +24,10 @@ it('end-to-end proof creation and verification (outer)', async () => { // bb.js part // // Proof creation - const prover = new Backend(assert_lt_program); - const proof = await prover.generateProof(witness); + const proof = await backend.generateProof(witness); // Proof verification - const isValid = await prover.verifyProof(proof); + const isValid = await backend.verifyProof(proof); expect(isValid).to.be.true; }); @@ -42,10 +43,9 @@ it('end-to-end proof creation and verification (outer) -- Verifier API', async ( const { witness } = await program.execute(inputs); // Generate proof - const prover = new Backend(assert_lt_program); - const proof = await prover.generateProof(witness); + const proof = await backend.generateProof(witness); - const verificationKey = await prover.getVerificationKey(); + const verificationKey = await backend.getVerificationKey(); // Proof verification const verifier = new Verifier(); @@ -68,11 +68,10 @@ it('end-to-end proof creation and verification (inner)', async () => { // bb.js part // // Proof creation - const prover = new Backend(assert_lt_program); - const proof = await prover.generateProof(witness); + const proof = await backend.generateProof(witness); // Proof verification - const isValid = await prover.verifyProof(proof); + const isValid = await backend.verifyProof(proof); expect(isValid).to.be.true; }); @@ -88,9 +87,7 @@ it('end-to-end proving and verification with different instances', async () => { const { witness } = await program.execute(inputs); // bb.js part - const prover = new Backend(assert_lt_program); - - const proof = await prover.generateProof(witness); + const proof = await backend.generateProof(witness); const verifier = new Backend(assert_lt_program); const proof_is_valid = await verifier.verifyProof(proof); @@ -119,18 +116,17 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', // // Proof creation // - const prover = new Backend(assert_lt_program); // Create a proof using both proving systems, the majority of the time // one would only use outer proofs. - const proofOuter = await prover.generateProof(witness); - const _proofInner = await prover.generateProof(witness); + const proofOuter = await backend.generateProof(witness); + const _proofInner = await backend.generateProof(witness); // Proof verification // - const isValidOuter = await prover.verifyProof(proofOuter); + const isValidOuter = await backend.verifyProof(proofOuter); expect(isValidOuter).to.be.true; // We can also try verifying an inner proof and it will fail. - const isValidInner = await prover.verifyProof(_proofInner); + const isValidInner = await backend.verifyProof(_proofInner); expect(isValidInner).to.be.true; }); @@ -147,10 +143,10 @@ it('end-to-end proof creation and verification for multiple ACIR circuits (inner // bb.js part // // Proof creation - const prover = new Backend(fold_fibonacci_program); - const proof = await prover.generateProof(witness); + const backend = new Backend(fold_fibonacci_program); + const proof = await backend.generateProof(witness); // Proof verification - const isValid = await prover.verifyProof(proof); + const isValid = await backend.verifyProof(proof); expect(isValid).to.be.true; }); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 8cb0db3ee55..3648881e501 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -154,8 +154,8 @@ impl<'a> Interpreter<'a> { let mut scope = Vec::new(); if self.scopes.len() > 1 { scope = self.scopes.drain(1..).collect(); - self.push_scope(); } + self.push_scope(); (std::mem::take(&mut self.in_loop), scope) } @@ -210,10 +210,11 @@ impl<'a> Interpreter<'a> { } }, HirPattern::Struct(struct_type, pattern_fields, _) => { + self.push_scope(); self.type_check(typ, &argument, location)?; self.type_check(struct_type, &argument, location)?; - match argument { + let res = match argument { Value::Struct(fields, struct_type) if fields.len() == pattern_fields.len() => { for (field_name, field_pattern) in pattern_fields { let field = fields.get(&field_name.0.contents).ok_or_else(|| { @@ -239,7 +240,9 @@ impl<'a> Interpreter<'a> { value, location, }), - } + }; + self.pop_scope(); + res } } } @@ -358,13 +361,14 @@ impl<'a> Interpreter<'a> { } DefinitionKind::Local(_) => self.lookup(&ident), DefinitionKind::Global(global_id) => { - // Don't need to check let_.comptime, we can evaluate non-comptime globals too. // Avoid resetting the value if it is already known if let Ok(value) = self.lookup(&ident) { Ok(value) } else { let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); - self.evaluate_let(let_)?; + if let_.comptime { + self.evaluate_let(let_.clone())?; + } self.lookup(&ident) } } @@ -1296,6 +1300,7 @@ impl<'a> Interpreter<'a> { Err(InterpreterError::Continue) => continue, Err(other) => return Err(other), } + self.pop_scope(); } diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs index 02010b6886d..f2e21e608ea 100644 --- a/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -59,7 +59,6 @@ impl<'interner> Interpreter<'interner> { self.scan_expression(let_.expression)?; } } - Ok(()) } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index f5c90b33c11..a90bd931496 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -598,6 +598,10 @@ impl Type { matches!(self.follow_bindings(), Type::FieldElement) } + pub fn is_bool(&self) -> bool { + matches!(self.follow_bindings(), Type::Bool) + } + pub fn is_signed(&self) -> bool { matches!(self.follow_bindings(), Type::Integer(Signedness::Signed, _)) } diff --git a/test_programs/benchmarks/bench_sha256/Nargo.toml b/test_programs/benchmarks/bench_sha256/Nargo.toml deleted file mode 100644 index 488b94ca858..00000000000 --- a/test_programs/benchmarks/bench_sha256/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "bench_sha256" -version = "0.1.0" -type = "bin" -authors = [""] - -[dependencies] diff --git a/tooling/noirc_abi_wasm/test/shared/abi_encode.ts b/tooling/noirc_abi_wasm/test/shared/abi_encode.ts index f4ab8175700..62eb7658f43 100644 --- a/tooling/noirc_abi_wasm/test/shared/abi_encode.ts +++ b/tooling/noirc_abi_wasm/test/shared/abi_encode.ts @@ -9,9 +9,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [{ start: 1, end: 2 }], bar: [{ start: 2, end: 4 }] }, return_type: null, - return_witnesses: [], error_types: {}, }; diff --git a/tooling/noirc_abi_wasm/test/shared/array_as_field.ts b/tooling/noirc_abi_wasm/test/shared/array_as_field.ts index 3698b913c66..8fecacf79f8 100644 --- a/tooling/noirc_abi_wasm/test/shared/array_as_field.ts +++ b/tooling/noirc_abi_wasm/test/shared/array_as_field.ts @@ -8,9 +8,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [{ start: 1, end: 3 }] }, return_type: null, - return_witnesses: [], error_types: {}, }; diff --git a/tooling/noirc_abi_wasm/test/shared/decode_error.ts b/tooling/noirc_abi_wasm/test/shared/decode_error.ts index 36eb18b5210..662e0acb416 100644 --- a/tooling/noirc_abi_wasm/test/shared/decode_error.ts +++ b/tooling/noirc_abi_wasm/test/shared/decode_error.ts @@ -15,9 +15,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [{ start: 1, end: 3 }] }, return_type: null, - return_witnesses: [], error_types: { [FAKE_FIELD_SELECTOR]: { error_kind: 'custom', diff --git a/tooling/noirc_abi_wasm/test/shared/field_as_array.ts b/tooling/noirc_abi_wasm/test/shared/field_as_array.ts index 4e3e2fd12a8..3610c51e6db 100644 --- a/tooling/noirc_abi_wasm/test/shared/field_as_array.ts +++ b/tooling/noirc_abi_wasm/test/shared/field_as_array.ts @@ -8,9 +8,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [{ start: 1, end: 3 }] }, return_type: null, - return_witnesses: [], error_types: {}, }; diff --git a/tooling/noirc_abi_wasm/test/shared/structs.ts b/tooling/noirc_abi_wasm/test/shared/structs.ts index ee666e40e87..6d098832b3a 100644 --- a/tooling/noirc_abi_wasm/test/shared/structs.ts +++ b/tooling/noirc_abi_wasm/test/shared/structs.ts @@ -47,13 +47,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { - struct_arg: [{ start: 1, end: 2 }], - struct_array_arg: [{ start: 2, end: 5 }], - nested_struct_arg: [{ start: 5, end: 6 }], - }, return_type: null, - return_witnesses: [], error_types: {}, }; diff --git a/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts b/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts index 82a3e3998ca..b7f1f221b48 100644 --- a/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts +++ b/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts @@ -8,9 +8,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [{ start: 1, end: 2 }] }, return_type: null, - return_witnesses: [], error_types: {}, }; From d6d0ae26d2fef083dc240539b834d934c84b0326 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 17 Jun 2024 14:00:31 -0400 Subject: [PATCH 36/57] feat!: remove `dep::` prefix (#4946) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/2307 ## Summary\* `dep::` is unnecessary to distinguish between internal and external crates, so this PR removes it. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Tom French --- ...te_note_hash_and_optionally_a_nullifier.rs | 8 ++-- .../src/transforms/contract_interface.rs | 29 ++++++----- aztec_macros/src/transforms/note_interface.rs | 20 +++----- aztec_macros/src/transforms/storage.rs | 4 +- aztec_macros/src/utils/ast_utils.rs | 2 +- .../circuits/assert_lt/src/main.nr | 3 -- .../circuits/recursion/src/main.nr | 2 - compiler/noirc_frontend/src/debug/mod.rs | 2 +- .../src/hir/def_collector/dc_crate.rs | 4 +- .../src/hir/resolution/import.rs | 48 +++++++++++++++---- .../noirc_frontend/src/noir_parser.lalrpop | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 2 +- .../noirc_frontend/src/parser/parser/path.rs | 9 ++-- .../wasm/test/fixtures/deps/lib-a/src/lib.nr | 2 +- .../fixtures/deps/lib-c/src/module/foo.nr | 2 +- .../test/fixtures/noir-contract/src/main.nr | 2 +- .../wasm/test/fixtures/with-deps/src/main.nr | 2 +- docs/docs/how_to/merkle-proof.mdx | 1 - .../docs/noir/concepts/data_types/integers.md | 1 - docs/docs/noir/concepts/data_types/slices.mdx | 4 +- docs/docs/noir/concepts/data_types/strings.md | 1 - docs/docs/noir/concepts/traits.md | 4 +- .../modules_packages_crates/dependencies.md | 12 ++--- .../standard_library/containers/hashmap.md | 2 +- .../cryptographic_primitives/ec_primitives.md | 2 +- .../cryptographic_primitives/eddsa.mdx | 2 +- docs/docs/noir/standard_library/recursion.md | 1 - examples/recursion/recurse_leaf/src/main.nr | 4 +- examples/recursion/recurse_node/src/main.nr | 4 +- .../bench_eddsa_poseidon/src/main.nr | 4 +- .../bench_poseidon_hash/src/main.nr | 2 +- .../bench_poseidon_hash_100/src/main.nr | 4 +- .../bench_poseidon_hash_30/src/main.nr | 4 +- .../benchmarks/bench_sha256_100/src/main.nr | 4 +- .../benchmarks/bench_sha256_30/src/main.nr | 4 +- .../array_length_defaulting/src/main.nr | 2 +- .../assert_constant_fail/src/main.nr | 2 +- .../brillig_nested_slices/src/main.nr | 2 +- .../dep_impl_primitive/src/main.nr | 2 +- .../dep_submodule_overlap/Nargo.toml | 8 ++++ .../dep_submodule_overlap/src/lib.nr | 3 ++ .../dep_submodule_overlap/src/main.nr | 9 ++++ .../compile_failure/depend_on_bin/src/main.nr | 2 +- .../negate_unsigned/src/main.nr | 2 - .../orphaned_trait_impl/src/main.nr | 2 +- .../overlapping_dep_and_mod/Nargo.toml | 6 +++ .../overlapping_dep_and_mod/bin/Nargo.toml | 8 ++++ .../overlapping_dep_and_mod/bin/Prover.toml | 0 .../overlapping_dep_and_mod/bin/src/main.nr | 12 +++++ .../overlapping_dep_and_mod/foo/Nargo.toml | 7 +++ .../overlapping_dep_and_mod/foo/src/lib.nr | 3 ++ .../restricted_bit_sizes/src/main.nr | 2 +- .../turbofish_generic_count/src/main.nr | 3 +- .../src/main.nr | 2 - .../ec_baby_jubjub/src/main.nr | 16 +++---- .../intrinsic_die/src/main.nr | 1 - .../method_call_regression/src/main.nr | 2 - .../overlapping_dep_and_mod/Nargo.toml | 6 +++ .../overlapping_dep_and_mod/bin/Nargo.toml | 8 ++++ .../overlapping_dep_and_mod/bin/src/main.nr | 9 ++++ .../overlapping_dep_and_mod/foo/Nargo.toml | 7 +++ .../overlapping_dep_and_mod/foo/src/lib.nr | 5 ++ .../reexports/src/main.nr | 2 +- .../regression_2099/src/main.nr | 16 +++---- .../regression_3635/src/main.nr | 2 - .../str_as_bytes/src/main.nr | 1 - .../trait_default_implementation/src/main.nr | 2 - .../trait_override_implementation/src/main.nr | 2 - .../compile_success_empty/traits/src/main.nr | 2 - .../compile_success_empty/vectors/src/main.nr | 2 +- .../workspace_reexport_bug/binary/src/main.nr | 2 +- .../workspace_reexport_bug/library/src/lib.nr | 2 +- .../div_by_zero_constants/src/main.nr | 2 - .../div_by_zero_numerator_witness/src/main.nr | 2 - .../div_by_zero_witness/src/main.nr | 1 - .../hashmap_load_factor/src/main.nr | 6 +-- .../execution_success/4_sub/src/main.nr | 1 - .../execution_success/5_over/src/main.nr | 1 - test_programs/execution_success/6/src/main.nr | 1 - .../execution_success/6_array/src/main.nr | 1 - test_programs/execution_success/7/src/main.nr | 1 - .../aes128_encrypt/src/main.nr | 2 - .../array_dynamic_blackbox_input/src/main.nr | 2 +- .../src/main.nr | 2 +- .../execution_success/bigint/src/main.nr | 10 ++-- .../execution_success/blake3/src/main.nr | 2 - .../brillig_blake2s/src/main.nr | 1 - .../brillig_blake3/src/main.nr | 2 - .../src/main.nr | 4 +- .../brillig_cow_regression/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 1 - .../brillig_ecdsa_secp256r1/src/main.nr | 1 - .../brillig_fns_as_values/src/main.nr | 2 - .../brillig_hash_to_field/src/main.nr | 1 - .../brillig_keccak/src/main.nr | 1 - .../brillig_oracle/src/main.nr | 4 +- .../brillig_pedersen/src/main.nr | 2 - .../brillig_sha256/src/main.nr | 1 - .../brillig_slices/src/main.nr | 2 +- .../conditional_1/src/main.nr | 2 - .../conditional_2/src/main.nr | 2 - .../src/main.nr | 2 - .../execution_success/databus/src/main.nr | 2 - .../diamond_deps_0/src/main.nr | 6 +-- .../double_verify_nested_proof/src/main.nr | 1 - .../double_verify_proof/src/main.nr | 1 - .../double_verify_proof_recursive/src/main.nr | 1 - .../ecdsa_secp256k1/src/main.nr | 2 - .../ecdsa_secp256r1/src/main.nr | 2 - .../ecdsa_secp256r1_3x/src/main.nr | 2 - .../execution_success/eddsa/src/main.nr | 12 ++--- .../embedded_curve_ops/src/main.nr | 2 - .../fold_numeric_generic_poseidon/src/main.nr | 2 +- .../execution_success/generics/src/main.nr | 2 +- .../hash_to_field/src/main.nr | 2 - .../execution_success/hashmap/src/main.nr | 8 ++-- .../execution_success/import/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 2 +- .../execution_success/keccak256/src/main.nr | 1 - .../merkle_insert/src/main.nr | 3 +- .../execution_success/modulus/src/main.nr | 2 - .../src/main.nr | 2 +- .../operator_overloading/src/main.nr | 4 +- .../overlapping_dep_and_mod/Nargo.toml | 6 +++ .../overlapping_dep_and_mod/bin/Nargo.toml | 8 ++++ .../overlapping_dep_and_mod/bin/Prover.toml | 0 .../overlapping_dep_and_mod/bin/src/main.nr | 14 ++++++ .../overlapping_dep_and_mod/foo/Nargo.toml | 7 +++ .../overlapping_dep_and_mod/foo/src/lib.nr | 5 ++ .../pedersen_check/src/main.nr | 2 - .../pedersen_commitment/src/main.nr | 1 - .../pedersen_hash/src/main.nr | 1 - .../poseidon_bn254_hash/src/main.nr | 4 +- .../poseidonsponge_x5_254/src/main.nr | 2 +- .../execution_success/prelude/src/main.nr | 4 +- .../regression_3051/src/main.nr | 4 +- .../regression_3394/src/main.nr | 2 - .../regression_4124/src/main.nr | 4 +- .../regression_4449/src/main.nr | 1 - .../regression_5045/src/main.nr | 6 +-- .../regression_5202/src/main.nr | 2 +- .../regression_5252/src/main.nr | 2 +- .../src/main.nr | 1 - .../execution_success/schnorr/src/main.nr | 15 ++---- .../execution_success/sha256/src/main.nr | 1 - .../execution_success/sha2_byte/src/main.nr | 1 - .../signed_comparison/src/main.nr | 2 - .../signed_division/src/main.nr | 1 - .../simple_print/src/main.nr | 1 - .../simple_shield/src/main.nr | 2 - .../slice_coercion/src/main.nr | 2 +- .../execution_success/slices/src/main.nr | 3 +- .../execution_success/strings/src/main.nr | 1 - .../execution_success/to_bits/src/main.nr | 2 - .../to_bytes_integration/src/main.nr | 2 - .../trait_method_mut_self/src/main.nr | 4 +- .../traits_in_crates_1/crate1/src/lib.nr | 2 +- .../traits_in_crates_1/src/main.nr | 2 +- .../traits_in_crates_2/crate2/src/lib.nr | 2 +- .../traits_in_crates_2/src/main.nr | 2 +- .../src/main.nr | 6 +-- .../execution_success/u128/src/main.nr | 2 - .../execution_success/unit_value/src/main.nr | 2 +- .../wrapping_operations/src/main.nr | 2 - test_programs/format.sh | 2 +- .../should_fail_mismatch/src/main.nr | 2 +- .../brillig_overflow_checks/src/main.nr | 2 +- .../embedded_curve_ops/src/main.nr | 2 +- .../field_comparisons/src/main.nr | 2 +- .../noir_test_success/mock_oracle/src/main.nr | 2 +- .../regression_4561/src/main.nr | 4 +- .../should_fail_with_matches/src/main.nr | 18 +++---- test_programs/rebuild.sh | 25 +--------- .../test_libraries/diamond_deps_1/src/lib.nr | 2 +- .../test_libraries/exporting_lib/src/lib.nr | 2 +- .../test_libraries/reexporting_lib/src/lib.nr | 4 +- tooling/debugger/src/source_code_printer.rs | 4 +- tooling/nargo_fmt/tests/expected/contract.nr | 6 +-- .../nargo_fmt/tests/expected/import_braces.nr | 2 +- tooling/nargo_fmt/tests/expected/let.nr | 8 ++-- tooling/nargo_fmt/tests/expected/print.nr | 2 - tooling/nargo_fmt/tests/expected/print2.nr | 2 - .../tests/expected/singleton_import.nr | 2 + tooling/nargo_fmt/tests/input/contract.nr | 6 +-- .../nargo_fmt/tests/input/import_braces.nr | 2 +- tooling/nargo_fmt/tests/input/let.nr | 8 ++-- tooling/nargo_fmt/tests/input/print.nr | 2 - tooling/nargo_fmt/tests/input/print2.nr | 2 - .../nargo_fmt/tests/input/singleton_import.nr | 2 + .../assert_lt/src/main.nr | 2 - 190 files changed, 375 insertions(+), 369 deletions(-) create mode 100644 test_programs/compile_failure/dep_submodule_overlap/Nargo.toml create mode 100644 test_programs/compile_failure/dep_submodule_overlap/src/lib.nr create mode 100644 test_programs/compile_failure/dep_submodule_overlap/src/main.nr create mode 100644 test_programs/compile_failure/overlapping_dep_and_mod/Nargo.toml create mode 100644 test_programs/compile_failure/overlapping_dep_and_mod/bin/Nargo.toml create mode 100644 test_programs/compile_failure/overlapping_dep_and_mod/bin/Prover.toml create mode 100644 test_programs/compile_failure/overlapping_dep_and_mod/bin/src/main.nr create mode 100644 test_programs/compile_failure/overlapping_dep_and_mod/foo/Nargo.toml create mode 100644 test_programs/compile_failure/overlapping_dep_and_mod/foo/src/lib.nr create mode 100644 test_programs/compile_success_empty/overlapping_dep_and_mod/Nargo.toml create mode 100644 test_programs/compile_success_empty/overlapping_dep_and_mod/bin/Nargo.toml create mode 100644 test_programs/compile_success_empty/overlapping_dep_and_mod/bin/src/main.nr create mode 100644 test_programs/compile_success_empty/overlapping_dep_and_mod/foo/Nargo.toml create mode 100644 test_programs/compile_success_empty/overlapping_dep_and_mod/foo/src/lib.nr create mode 100644 test_programs/execution_success/overlapping_dep_and_mod/Nargo.toml create mode 100644 test_programs/execution_success/overlapping_dep_and_mod/bin/Nargo.toml create mode 100644 test_programs/execution_success/overlapping_dep_and_mod/bin/Prover.toml create mode 100644 test_programs/execution_success/overlapping_dep_and_mod/bin/src/main.nr create mode 100644 test_programs/execution_success/overlapping_dep_and_mod/foo/Nargo.toml create mode 100644 test_programs/execution_success/overlapping_dep_and_mod/foo/src/lib.nr create mode 100644 tooling/nargo_fmt/tests/expected/singleton_import.nr create mode 100644 tooling/nargo_fmt/tests/input/singleton_import.nr diff --git a/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs b/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs index 30c0f63a2d4..40fde39a06f 100644 --- a/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs +++ b/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs @@ -176,7 +176,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( format!( " unconstrained fn compute_note_hash_and_optionally_a_nullifier( - contract_address: dep::aztec::protocol_types::address::AztecAddress, + contract_address: aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, @@ -194,7 +194,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( let if_statements: Vec = note_types.iter().map(|note_type| format!( "if (note_type_id == {0}::get_note_type_id()) {{ - dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier({0}::deserialize_content, note_header, compute_nullifier, serialized_note) + aztec::note::utils::compute_note_hash_and_optionally_a_nullifier({0}::deserialize_content, note_header, compute_nullifier, serialized_note) }}" , note_type)).collect(); @@ -208,14 +208,14 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( format!( " unconstrained fn compute_note_hash_and_optionally_a_nullifier( - contract_address: dep::aztec::protocol_types::address::AztecAddress, + contract_address: aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, compute_nullifier: bool, serialized_note: [Field; {}], ) -> pub [Field; 4] {{ - let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); + let note_header = aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); {} }}", diff --git a/aztec_macros/src/transforms/contract_interface.rs b/aztec_macros/src/transforms/contract_interface.rs index 90f9ce6164a..e6ac43ad2c4 100644 --- a/aztec_macros/src/transforms/contract_interface.rs +++ b/aztec_macros/src/transforms/contract_interface.rs @@ -30,8 +30,8 @@ use crate::utils::{ // for i in 0..third_arg.len() { // args_acc = args_acc.append(third_arg[i].serialize().as_slice()); // } -// let args_hash = dep::aztec::hash::hash_args(args_acc); -// assert(args_hash == dep::aztec::oracle::arguments::pack_arguments(args_acc)); +// let args_hash = aztec::hash::hash_args(args_acc); +// assert(args_hash == aztec::oracle::arguments::pack_arguments(args_acc)); // PublicCallInterface { // target_contract: self.target_contract, // selector: FunctionSelector::from_signature("SELECTOR_PLACEHOLDER"), @@ -56,7 +56,10 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call .join(", "); let fn_return_type: noirc_frontend::ast::UnresolvedType = func.return_type(); - let fn_selector = format!("dep::aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature(\"{}\")", SELECTOR_PLACEHOLDER); + let fn_selector = format!( + "aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature(\"{}\")", + SELECTOR_PLACEHOLDER + ); let parameters = func.parameters(); let is_void = if matches!(fn_return_type.typ, UnresolvedTypeData::Unit) { "Void" } else { "" }; @@ -93,8 +96,8 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call format!( "let mut args_acc: [Field] = &[]; {} - let args_hash = dep::aztec::hash::hash_args(args_acc); - assert(args_hash == dep::aztec::oracle::arguments::pack_arguments(args_acc));", + let args_hash = aztec::hash::hash_args(args_acc); + assert(args_hash == aztec::oracle::arguments::pack_arguments(args_acc));", call_args ) } else { @@ -103,7 +106,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call let fn_body = format!( "{} - dep::aztec::context::{}{}{}CallInterface {{ + aztec::context::{}{}{}CallInterface {{ target_contract: self.target_contract, selector: {}, args_hash, @@ -111,7 +114,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call args_hash, aztec_visibility, is_static, is_void, fn_selector, ); format!( - "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface{} {{ + "pub fn {}(self, {}) -> aztec::context::{}{}{}CallInterface{} {{ {} }}", fn_name, fn_parameters, aztec_visibility, is_static, is_void, return_type_hint, fn_body @@ -125,16 +128,16 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call ); let fn_body = format!( "{} - dep::aztec::context::Public{}{}CallInterface {{ + aztec::context::Public{}{}CallInterface {{ target_contract: self.target_contract, selector: {}, args: args_acc, - gas_opts: dep::aztec::context::gas::GasOpts::default(), + gas_opts: aztec::context::gas::GasOpts::default(), }}", args, is_static, is_void, fn_selector, ); format!( - "pub fn {}(self, {}) -> dep::aztec::context::Public{}{}CallInterface{} {{ + "pub fn {}(self, {}) -> aztec::context::Public{}{}CallInterface{} {{ {} }}", fn_name, fn_parameters, is_static, is_void, return_type_hint, fn_body @@ -153,14 +156,14 @@ pub fn generate_contract_interface( let contract_interface = format!( " struct {0} {{ - target_contract: dep::aztec::protocol_types::address::AztecAddress + target_contract: aztec::protocol_types::address::AztecAddress }} impl {0} {{ {1} pub fn at( - target_contract: dep::aztec::protocol_types::address::AztecAddress + target_contract: aztec::protocol_types::address::AztecAddress ) -> Self {{ Self {{ target_contract }} }} @@ -168,7 +171,7 @@ pub fn generate_contract_interface( #[contract_library_method] pub fn at( - target_contract: dep::aztec::protocol_types::address::AztecAddress + target_contract: aztec::protocol_types::address::AztecAddress ) -> {0} {{ {0} {{ target_contract }} }} diff --git a/aztec_macros/src/transforms/note_interface.rs b/aztec_macros/src/transforms/note_interface.rs index fdce8b81db2..f0e7d0d5034 100644 --- a/aztec_macros/src/transforms/note_interface.rs +++ b/aztec_macros/src/transforms/note_interface.rs @@ -268,7 +268,7 @@ fn generate_note_get_header( ) -> Result { let function_source = format!( " - fn get_header(note: {}) -> dep::aztec::note::note_header::NoteHeader {{ + fn get_header(note: {}) -> aztec::note::note_header::NoteHeader {{ note.{} }} ", @@ -299,7 +299,7 @@ fn generate_note_set_header( ) -> Result { let function_source = format!( " - fn set_header(self: &mut {}, header: dep::aztec::note::note_header::NoteHeader) {{ + fn set_header(self: &mut {}, header: aztec::note::note_header::NoteHeader) {{ self.{} = header; }} ", @@ -488,7 +488,7 @@ fn generate_note_properties_fn( // Automatically generate the method to compute the note's content hash as: // fn compute_note_content_hash(self: NoteType) -> Field { -// dep::aztec::hash::pedersen_hash(self.serialize_content(), dep::aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) +// aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) // } // fn generate_compute_note_content_hash( @@ -498,7 +498,7 @@ fn generate_compute_note_content_hash( let function_source = format!( " fn compute_note_content_hash(self: {}) -> Field {{ - dep::aztec::hash::pedersen_hash(self.serialize_content(), dep::aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) + aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) }} ", note_type @@ -557,10 +557,7 @@ fn generate_note_properties_struct_source( .iter() .filter_map(|(field_name, _)| { if field_name != note_header_field_name { - Some(format!( - "{}: dep::aztec::note::note_getter_options::PropertySelector", - field_name - )) + Some(format!("{}: aztec::note::note_getter_options::PropertySelector", field_name)) } else { None } @@ -588,7 +585,7 @@ fn generate_note_properties_fn_source( .filter_map(|(index, (field_name, _))| { if field_name != note_header_field_name { Some(format!( - "{}: dep::aztec::note::note_getter_options::PropertySelector {{ index: {}, offset: 0, length: 32 }}", + "{}: aztec::note::note_getter_options::PropertySelector {{ index: {}, offset: 0, length: 32 }}", field_name, index )) @@ -665,10 +662,7 @@ fn generate_note_deserialize_content_source( ) } } else { - format!( - "{}: dep::aztec::note::note_header::NoteHeader::empty()", - note_header_field_name - ) + format!("{}: aztec::note::note_header::NoteHeader::empty()", note_header_field_name) } }) .collect::>() diff --git a/aztec_macros/src/transforms/storage.rs b/aztec_macros/src/transforms/storage.rs index a1c21c7efcf..8b778b4cca6 100644 --- a/aztec_macros/src/transforms/storage.rs +++ b/aztec_macros/src/transforms/storage.rs @@ -509,10 +509,10 @@ pub fn generate_storage_layout( let mut storable_fields_impl = vec![]; definition.fields.iter().enumerate().for_each(|(index, (field_ident, field_type))| { - storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident, index)); + storable_fields.push(format!("{}: aztec::prelude::Storable", field_ident, index)); generic_args.push(format!("N{}", index)); storable_fields_impl.push(format!( - "{}: dep::aztec::prelude::Storable {{ slot: 0, typ: \"{}\" }}", + "{}: aztec::prelude::Storable {{ slot: 0, typ: \"{}\" }}", field_ident, field_type.to_string().replace("plain::", "") )); diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 48b3b25747b..4467c4bca4b 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -161,7 +161,7 @@ macro_rules! chained_dep { ( $base:expr $(, $tail:expr)* ) => { { let mut base_path = ident_path($base); - base_path.kind = PathKind::Dep; + base_path.kind = PathKind::Plain; $( base_path.segments.push(ident($tail)); )* diff --git a/compiler/integration-tests/circuits/assert_lt/src/main.nr b/compiler/integration-tests/circuits/assert_lt/src/main.nr index b8e255ca492..47e229d6c8b 100644 --- a/compiler/integration-tests/circuits/assert_lt/src/main.nr +++ b/compiler/integration-tests/circuits/assert_lt/src/main.nr @@ -1,10 +1,7 @@ -use dep::std; - fn main(x: u64, y: pub u64) -> pub u64 { // We include a println statement to show that noirJS will ignore this and continue execution std::println("foo"); - assert(x < y); x + y } diff --git a/compiler/integration-tests/circuits/recursion/src/main.nr b/compiler/integration-tests/circuits/recursion/src/main.nr index 173207766fb..94cae14daa7 100644 --- a/compiler/integration-tests/circuits/recursion/src/main.nr +++ b/compiler/integration-tests/circuits/recursion/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main( verification_key: [Field; 114], proof: [Field; 93], diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index f5866f5b756..443267380b5 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -470,7 +470,7 @@ impl DebugInstrumenter { .join(",\n"); let (program, errors) = parse_program(&format!( r#" - use dep::__debug::{{ + use __debug::{{ __debug_var_assign, __debug_var_drop, __debug_fn_enter, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 4511537a1cc..4d64481bc9f 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -468,7 +468,7 @@ fn inject_prelude( let path = Path { segments: segments.clone(), - kind: crate::ast::PathKind::Dep, + kind: crate::ast::PathKind::Plain, span: Span::default(), }; @@ -489,7 +489,7 @@ fn inject_prelude( 0, ImportDirective { module_id: crate_root, - path: Path { segments, kind: PathKind::Dep, span: Span::default() }, + path: Path { segments, kind: PathKind::Plain, span: Span::default() }, alias: None, is_prelude: true, }, diff --git a/compiler/noirc_frontend/src/hir/resolution/import.rs b/compiler/noirc_frontend/src/hir/resolution/import.rs index 343113836ed..f3dae31e1e0 100644 --- a/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -158,16 +158,34 @@ fn resolve_path_to_ns( allow_contracts, ) } - crate::ast::PathKind::Dep => resolve_external_dep( - def_map, - import_directive, - def_maps, - allow_contracts, - importing_crate, - ), crate::ast::PathKind::Plain => { - // Plain paths are only used to import children modules. It's possible to allow import of external deps, but maybe this distinction is better? - // In Rust they can also point to external Dependencies, if no children can be found with the specified name + // There is a possibility that the import path is empty + // In that case, early return + if import_path.is_empty() { + return resolve_name_in_module( + crate_id, + importing_crate, + import_path, + import_directive.module_id, + def_maps, + allow_contracts, + ); + } + + let current_mod_id = ModuleId { krate: crate_id, local_id: import_directive.module_id }; + let current_mod = &def_map.modules[current_mod_id.local_id.0]; + let first_segment = import_path.first().expect("ice: could not fetch first segment"); + if current_mod.find_name(first_segment).is_none() { + // Resolve externally when first segment is unresolved + return resolve_external_dep( + def_map, + import_directive, + def_maps, + allow_contracts, + importing_crate, + ); + } + resolve_name_in_module( crate_id, importing_crate, @@ -177,6 +195,14 @@ fn resolve_path_to_ns( allow_contracts, ) } + + crate::ast::PathKind::Dep => resolve_external_dep( + def_map, + import_directive, + def_maps, + allow_contracts, + importing_crate, + ), } } @@ -302,7 +328,9 @@ fn resolve_external_dep( .ok_or_else(|| PathResolutionError::Unresolved(crate_name.to_owned()))?; // Create an import directive for the dependency crate - let path_without_crate_name = &path[1..]; // XXX: This will panic if the path is of the form `use dep::std` Ideal algorithm will not distinguish between crate and module + // XXX: This will panic if the path is of the form `use std`. Ideal algorithm will not distinguish between crate and module + // See `singleton_import.nr` test case for a check that such cases are handled elsewhere. + let path_without_crate_name = &path[1..]; let path = Path { segments: path_without_crate_name.to_vec(), diff --git a/compiler/noirc_frontend/src/noir_parser.lalrpop b/compiler/noirc_frontend/src/noir_parser.lalrpop index c6cb788a5a4..5768e3c1d17 100644 --- a/compiler/noirc_frontend/src/noir_parser.lalrpop +++ b/compiler/noirc_frontend/src/noir_parser.lalrpop @@ -125,7 +125,7 @@ pub(crate) Path: Path = { }, "dep" "::" => { - let kind = PathKind::Dep; + let kind = PathKind::Plain; let span = Span::from(lo as u32..hi as u32); Path { segments, kind, span } }, diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 0d8215f2ea4..e8838c58772 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1445,7 +1445,7 @@ mod test { "use foo::{bar, hello}", "use foo::{bar as bar2, hello}", "use foo::{bar as bar2, hello::{foo}, nested::{foo, bar}}", - "use dep::{std::println, bar::baz}", + "use std::{println, bar::baz}", ]; let invalid_use_statements = [ diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 47bb11991fa..e40268af410 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -25,7 +25,7 @@ fn empty_path() -> impl NoirParser { let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); - choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep))) + choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Plain))) } pub(super) fn maybe_empty_path() -> impl NoirParser { @@ -43,7 +43,8 @@ mod test { ("std", vec!["std"]), ("std::hash", vec!["std", "hash"]), ("std::hash::collections", vec!["std", "hash", "collections"]), - ("dep::foo::bar", vec!["foo", "bar"]), + ("foo::bar", vec!["foo", "bar"]), + ("foo::bar", vec!["foo", "bar"]), ("crate::std::hash", vec!["std", "hash"]), ]; @@ -61,7 +62,7 @@ mod test { fn parse_path_kinds() { let cases = vec![ ("std", PathKind::Plain), - ("dep::hash::collections", PathKind::Dep), + ("hash::collections", PathKind::Plain), ("crate::std::hash", PathKind::Crate), ]; @@ -72,7 +73,7 @@ mod test { parse_all_failing( path(), - vec!["dep", "crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], + vec!["crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], ); } } diff --git a/compiler/wasm/test/fixtures/deps/lib-a/src/lib.nr b/compiler/wasm/test/fixtures/deps/lib-a/src/lib.nr index 3f8fa051daf..c38188d0119 100644 --- a/compiler/wasm/test/fixtures/deps/lib-a/src/lib.nr +++ b/compiler/wasm/test/fixtures/deps/lib-a/src/lib.nr @@ -1,4 +1,4 @@ -use dep::lib_b::assert_non_zero; +use lib_b::assert_non_zero; pub fn divide(a: u64, b: u64) -> u64 { assert_non_zero(b); diff --git a/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr b/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr index 0376cd4cb87..23b6659b3c5 100644 --- a/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr +++ b/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr @@ -1,3 +1,3 @@ pub fn bar(param: Field) -> Field { - dep::std::hash::pedersen_hash([param]) + std::hash::pedersen_hash([param]) } diff --git a/compiler/wasm/test/fixtures/noir-contract/src/main.nr b/compiler/wasm/test/fixtures/noir-contract/src/main.nr index fc1dc8a5a17..6f63850e3a0 100644 --- a/compiler/wasm/test/fixtures/noir-contract/src/main.nr +++ b/compiler/wasm/test/fixtures/noir-contract/src/main.nr @@ -1,5 +1,5 @@ contract TestContract { - use dep::test::module::foo; + use test::module::foo; fn constructor(param: Field, pub_param: pub Field) -> pub [Field; 2] { [foo::bar(param), param + pub_param] diff --git a/compiler/wasm/test/fixtures/with-deps/src/main.nr b/compiler/wasm/test/fixtures/with-deps/src/main.nr index 056bcc180b4..fe9e7f9ca77 100644 --- a/compiler/wasm/test/fixtures/with-deps/src/main.nr +++ b/compiler/wasm/test/fixtures/with-deps/src/main.nr @@ -1,4 +1,4 @@ -use dep::lib_a::divide; +use lib_a::divide; fn main(x: u64, y: pub u64) { divide(x, y); } diff --git a/docs/docs/how_to/merkle-proof.mdx b/docs/docs/how_to/merkle-proof.mdx index 16c425bed76..0a128adb2de 100644 --- a/docs/docs/how_to/merkle-proof.mdx +++ b/docs/docs/how_to/merkle-proof.mdx @@ -12,7 +12,6 @@ Let's walk through an example of a merkle membership proof in Noir that proves t in a merkle tree. ```rust -use dep::std; fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { let leaf = std::hash::hash_to_field(message.as_slice()); diff --git a/docs/docs/noir/concepts/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md index c14fffa7174..a1d59bf3166 100644 --- a/docs/docs/noir/concepts/data_types/integers.md +++ b/docs/docs/noir/concepts/data_types/integers.md @@ -149,7 +149,6 @@ fn wrapping_mul(x: T, y: T) -> T; Example of how it is used: ```rust -use dep::std; fn main(x: u8, y: u8) -> pub u8 { std::wrapping_add(x, y) diff --git a/docs/docs/noir/concepts/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx index 4eccc677b80..dff08d63ffb 100644 --- a/docs/docs/noir/concepts/data_types/slices.mdx +++ b/docs/docs/noir/concepts/data_types/slices.mdx @@ -12,9 +12,7 @@ import Experimental from '@site/src/components/Notes/_experimental.mdx'; A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. ```rust -use dep::std::slice; - -fn main() -> pub Field { +fn main() -> pub u32 { let mut slice: [Field] = &[0; 2]; let mut new_slice = slice.push_back(6); diff --git a/docs/docs/noir/concepts/data_types/strings.md b/docs/docs/noir/concepts/data_types/strings.md index 8ab5825a4c4..1fdee42425e 100644 --- a/docs/docs/noir/concepts/data_types/strings.md +++ b/docs/docs/noir/concepts/data_types/strings.md @@ -20,7 +20,6 @@ You can use strings in `assert()` functions or print them with `println()`. See more about [Logging](../../standard_library/logging.md). ```rust -use dep::std; fn main(message : pub str<11>, hex_as_string : str<4>) { println(message); diff --git a/docs/docs/noir/concepts/traits.md b/docs/docs/noir/concepts/traits.md index ef1445a5907..df7cb9ebda0 100644 --- a/docs/docs/noir/concepts/traits.md +++ b/docs/docs/noir/concepts/traits.md @@ -372,13 +372,13 @@ impls for any trait we need on it. ```rust struct Wrapper { - foo: dep::some_library::Foo, + foo: some_library::Foo, } impl Default for Wrapper { fn default() -> Wrapper { Wrapper { - foo: dep::some_library::Foo::new(), + foo: some_library::Foo::new(), } } } diff --git a/docs/docs/noir/modules_packages_crates/dependencies.md b/docs/docs/noir/modules_packages_crates/dependencies.md index 2c028d85853..24e02de08fe 100644 --- a/docs/docs/noir/modules_packages_crates/dependencies.md +++ b/docs/docs/noir/modules_packages_crates/dependencies.md @@ -70,15 +70,15 @@ You can import a dependency to a Noir file using the following syntax. For examp ecrecover-noir library and local lib_a referenced above: ```rust -use dep::ecrecover; -use dep::lib_a; +use ecrecover; +use lib_a; ``` You can also import only the specific parts of dependency that you want to use, like so: ```rust -use dep::std::hash::sha256; -use dep::std::scalar_mul::fixed_base_embedded_curve; +use std::hash::sha256; +use std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the @@ -86,7 +86,7 @@ Lastly, as demonstrated in the can import multiple items in the same line by enclosing them in curly braces: ```rust -use dep::std::ec::tecurve::affine::{Curve, Point}; +use std::ec::tecurve::affine::{Curve, Point}; ``` We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. @@ -100,7 +100,7 @@ Note that when you import a dependency, you also get access to all of the depend For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: ```rust -use dep::phy_vector; +use phy_vector; fn main(x : Field, y : pub Field) { //... diff --git a/docs/docs/noir/standard_library/containers/hashmap.md b/docs/docs/noir/standard_library/containers/hashmap.md index 2b9f4895722..651e7f5723b 100644 --- a/docs/docs/noir/standard_library/containers/hashmap.md +++ b/docs/docs/noir/standard_library/containers/hashmap.md @@ -21,7 +21,7 @@ Example: ```rust // Create a mapping from Fields to u32s with a maximum length of 12 // using a poseidon2 hasher -use dep::std::hash::poseidon2::Poseidon2Hasher; +use std::hash::poseidon2::Poseidon2Hasher; let mut map: HashMap> = HashMap::default(); map.insert(1, 2); diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md index d2b42d67b7c..f839b4a228e 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ b/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -81,7 +81,7 @@ from the private key. This is a matter of using scalar multiplication. In the ca for example, this code would do: ```rust -use dep::std::ec::tecurve::affine::{Curve, Point}; +use std::ec::tecurve::affine::{Curve, Point}; fn bjj_pub_key(priv_key: Field) -> Point { diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx index 789d26ce426..1ad42a5ac96 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -18,7 +18,7 @@ fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. For instance, if you want to use Poseidon2 instead, you can do the following: ```rust -use dep::std::hash::poseidon2::Poseidon2Hasher; +use std::hash::poseidon2::Poseidon2Hasher; eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); ``` diff --git a/docs/docs/noir/standard_library/recursion.md b/docs/docs/noir/standard_library/recursion.md index cb3e7a9fcdf..8cfb37fc52d 100644 --- a/docs/docs/noir/standard_library/recursion.md +++ b/docs/docs/noir/standard_library/recursion.md @@ -40,7 +40,6 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [F ## Example usage ```rust -use dep::std; fn main( verification_key : [Field; 114], diff --git a/examples/recursion/recurse_leaf/src/main.nr b/examples/recursion/recurse_leaf/src/main.nr index b6a2b49b219..4859e84d49e 100644 --- a/examples/recursion/recurse_leaf/src/main.nr +++ b/examples/recursion/recurse_leaf/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - #[recursive] fn main( verification_key: [Field; 114], @@ -17,4 +15,4 @@ fn main( ); // Take output of previous proof and add another number to it. public_inputs[2] as u64 + num -} \ No newline at end of file +} diff --git a/examples/recursion/recurse_node/src/main.nr b/examples/recursion/recurse_node/src/main.nr index 7c983dcf050..60192493b54 100644 --- a/examples/recursion/recurse_node/src/main.nr +++ b/examples/recursion/recurse_node/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main( verification_key: [Field; 114], public_inputs: pub [Field; 4], @@ -14,4 +12,4 @@ fn main( key_hash ); public_inputs[3] as u64 -} \ No newline at end of file +} diff --git a/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr b/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr index 31c2f1f2d13..cb853e48c30 100644 --- a/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr +++ b/test_programs/benchmarks/bench_eddsa_poseidon/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::eddsa::{eddsa_poseidon_verify}; +use std::eddsa::eddsa_poseidon_verify; fn main( msg: pub Field, @@ -9,4 +9,4 @@ fn main( s: Field ) -> pub bool { eddsa_poseidon_verify(pub_key_x, pub_key_y, s, r8_x, r8_y, msg) -} \ No newline at end of file +} diff --git a/test_programs/benchmarks/bench_poseidon_hash/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash/src/main.nr index 38adeef6ec7..9900e91c1d7 100644 --- a/test_programs/benchmarks/bench_poseidon_hash/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash::poseidon; +use std::hash::poseidon; fn main(input: [Field; 2]) -> pub Field { poseidon::bn254::hash_2(input) diff --git a/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr index fc9a5b7a970..1c9bbfe61bf 100644 --- a/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash; +use std::hash; global SIZE = 100; @@ -9,4 +9,4 @@ fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { } results -} \ No newline at end of file +} diff --git a/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr index 4d2d94e4946..3edb47e9f72 100644 --- a/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash; +use std::hash; global SIZE = 30; @@ -9,4 +9,4 @@ fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { } results -} \ No newline at end of file +} diff --git a/test_programs/benchmarks/bench_sha256_100/src/main.nr b/test_programs/benchmarks/bench_sha256_100/src/main.nr index d78ca8002d2..6df856a83fc 100644 --- a/test_programs/benchmarks/bench_sha256_100/src/main.nr +++ b/test_programs/benchmarks/bench_sha256_100/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - global SIZE = 100; fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { @@ -9,4 +7,4 @@ fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { } results -} \ No newline at end of file +} diff --git a/test_programs/benchmarks/bench_sha256_30/src/main.nr b/test_programs/benchmarks/bench_sha256_30/src/main.nr index fa66d626586..220c1cfbbed 100644 --- a/test_programs/benchmarks/bench_sha256_30/src/main.nr +++ b/test_programs/benchmarks/bench_sha256_30/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - global SIZE = 30; fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { @@ -9,4 +7,4 @@ fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { } results -} \ No newline at end of file +} diff --git a/test_programs/compile_failure/array_length_defaulting/src/main.nr b/test_programs/compile_failure/array_length_defaulting/src/main.nr index 216a9ae3f0c..40543db2870 100644 --- a/test_programs/compile_failure/array_length_defaulting/src/main.nr +++ b/test_programs/compile_failure/array_length_defaulting/src/main.nr @@ -1,5 +1,5 @@ fn main() { - let x = dep::std::unsafe::zeroed(); + let x = std::unsafe::zeroed(); foo(x); } diff --git a/test_programs/compile_failure/assert_constant_fail/src/main.nr b/test_programs/compile_failure/assert_constant_fail/src/main.nr index cf682607083..b8d5d255228 100644 --- a/test_programs/compile_failure/assert_constant_fail/src/main.nr +++ b/test_programs/compile_failure/assert_constant_fail/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::assert_constant; +use std::assert_constant; fn main(x: Field) { foo(5, x); diff --git a/test_programs/compile_failure/brillig_nested_slices/src/main.nr b/test_programs/compile_failure/brillig_nested_slices/src/main.nr index 3d8a6748ccf..ee61195cf8f 100644 --- a/test_programs/compile_failure/brillig_nested_slices/src/main.nr +++ b/test_programs/compile_failure/brillig_nested_slices/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::slice; +use std::slice; // Tests nested slice passing to/from functions unconstrained fn push_back_to_slice(slice: [T], item: T) -> [T] { slice.push_back(item) diff --git a/test_programs/compile_failure/dep_impl_primitive/src/main.nr b/test_programs/compile_failure/dep_impl_primitive/src/main.nr index e61ae82b62c..40578574c75 100644 --- a/test_programs/compile_failure/dep_impl_primitive/src/main.nr +++ b/test_programs/compile_failure/dep_impl_primitive/src/main.nr @@ -1,4 +1,4 @@ -use dep::bad_impl; +use bad_impl; fn main(x: Field) { x.something(); diff --git a/test_programs/compile_failure/dep_submodule_overlap/Nargo.toml b/test_programs/compile_failure/dep_submodule_overlap/Nargo.toml new file mode 100644 index 00000000000..0d5a6221ef2 --- /dev/null +++ b/test_programs/compile_failure/dep_submodule_overlap/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dep_submodule_overlap" +type = "bin" +authors = [""] +compiler_version = ">=0.28.0" + +[dependencies] +reexporting_lib = { path = "../../test_libraries/reexporting_lib" } diff --git a/test_programs/compile_failure/dep_submodule_overlap/src/lib.nr b/test_programs/compile_failure/dep_submodule_overlap/src/lib.nr new file mode 100644 index 00000000000..e2e82b2f5cd --- /dev/null +++ b/test_programs/compile_failure/dep_submodule_overlap/src/lib.nr @@ -0,0 +1,3 @@ +struct MyStruct { + inner: Field +} diff --git a/test_programs/compile_failure/dep_submodule_overlap/src/main.nr b/test_programs/compile_failure/dep_submodule_overlap/src/main.nr new file mode 100644 index 00000000000..c53630c53ca --- /dev/null +++ b/test_programs/compile_failure/dep_submodule_overlap/src/main.nr @@ -0,0 +1,9 @@ +use reexporting_lib::{MyStruct, lib}; + +mod lib; +use crate::lib::MyStruct; + +fn main() { + let x = MyStruct { inner: 0 }; + assert(lib::is_struct_zero(x)); +} diff --git a/test_programs/compile_failure/depend_on_bin/src/main.nr b/test_programs/compile_failure/depend_on_bin/src/main.nr index 4e03e8eb41e..d7aff600fe6 100644 --- a/test_programs/compile_failure/depend_on_bin/src/main.nr +++ b/test_programs/compile_failure/depend_on_bin/src/main.nr @@ -1,4 +1,4 @@ -use dep::bin_dep; +use bin_dep; fn main(x : Field) { assert(x == 1); diff --git a/test_programs/compile_failure/negate_unsigned/src/main.nr b/test_programs/compile_failure/negate_unsigned/src/main.nr index db5f9b0820f..4d3c5abe5a4 100644 --- a/test_programs/compile_failure/negate_unsigned/src/main.nr +++ b/test_programs/compile_failure/negate_unsigned/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { let var = -1 as u8; std::println(var); diff --git a/test_programs/compile_failure/orphaned_trait_impl/src/main.nr b/test_programs/compile_failure/orphaned_trait_impl/src/main.nr index dfd88d8f074..dd04aa454b2 100644 --- a/test_programs/compile_failure/orphaned_trait_impl/src/main.nr +++ b/test_programs/compile_failure/orphaned_trait_impl/src/main.nr @@ -1,4 +1,4 @@ -impl dep::crate1::MyTrait for dep::crate2::MyStruct { +impl crate1::MyTrait for crate2::MyStruct { } fn main(x: Field, y: pub Field) { diff --git a/test_programs/compile_failure/overlapping_dep_and_mod/Nargo.toml b/test_programs/compile_failure/overlapping_dep_and_mod/Nargo.toml new file mode 100644 index 00000000000..b2c3e5f94be --- /dev/null +++ b/test_programs/compile_failure/overlapping_dep_and_mod/Nargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "bin", + "foo", +] +default-member = "bin" diff --git a/test_programs/compile_failure/overlapping_dep_and_mod/bin/Nargo.toml b/test_programs/compile_failure/overlapping_dep_and_mod/bin/Nargo.toml new file mode 100644 index 00000000000..57e704462db --- /dev/null +++ b/test_programs/compile_failure/overlapping_dep_and_mod/bin/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "overlapping_dep_and_mod" +type = "bin" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] +foo = { path = "../foo" } diff --git a/test_programs/compile_failure/overlapping_dep_and_mod/bin/Prover.toml b/test_programs/compile_failure/overlapping_dep_and_mod/bin/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/compile_failure/overlapping_dep_and_mod/bin/src/main.nr b/test_programs/compile_failure/overlapping_dep_and_mod/bin/src/main.nr new file mode 100644 index 00000000000..675e889b7e5 --- /dev/null +++ b/test_programs/compile_failure/overlapping_dep_and_mod/bin/src/main.nr @@ -0,0 +1,12 @@ +fn main() -> pub Field { + assert(foo::bar() + foo::baz() == 3); + assert(foo::bar() == 1); + assert(foo::baz() == 2); + foo::bar() + foo::baz() +} + +mod foo { + pub(crate) fn bar() -> Field { + 1 + } +} diff --git a/test_programs/compile_failure/overlapping_dep_and_mod/foo/Nargo.toml b/test_programs/compile_failure/overlapping_dep_and_mod/foo/Nargo.toml new file mode 100644 index 00000000000..857d4e722a8 --- /dev/null +++ b/test_programs/compile_failure/overlapping_dep_and_mod/foo/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "foo" +type = "lib" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] diff --git a/test_programs/compile_failure/overlapping_dep_and_mod/foo/src/lib.nr b/test_programs/compile_failure/overlapping_dep_and_mod/foo/src/lib.nr new file mode 100644 index 00000000000..7834e2c9276 --- /dev/null +++ b/test_programs/compile_failure/overlapping_dep_and_mod/foo/src/lib.nr @@ -0,0 +1,3 @@ +pub fn baz() -> Field { + 2 +} diff --git a/test_programs/compile_failure/restricted_bit_sizes/src/main.nr b/test_programs/compile_failure/restricted_bit_sizes/src/main.nr index 01e72bfcfd7..a3fea13cc3a 100644 --- a/test_programs/compile_failure/restricted_bit_sizes/src/main.nr +++ b/test_programs/compile_failure/restricted_bit_sizes/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::assert_constant; +use std::assert_constant; fn main() -> pub u63 { 5 diff --git a/test_programs/compile_failure/turbofish_generic_count/src/main.nr b/test_programs/compile_failure/turbofish_generic_count/src/main.nr index a360641fa15..4091b2f0581 100644 --- a/test_programs/compile_failure/turbofish_generic_count/src/main.nr +++ b/test_programs/compile_failure/turbofish_generic_count/src/main.nr @@ -1,4 +1,3 @@ - struct Bar { one: Field, two: Field, @@ -7,7 +6,7 @@ struct Bar { impl Bar { fn zeroed(_self: Self) -> A { - dep::std::unsafe::zeroed() + std::unsafe::zeroed() } } diff --git a/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr b/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr index 5446cfbb1e4..9b5d95c11bc 100644 --- a/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr +++ b/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { //Regression for to_le_bits() constant evaluation // binary array representation of u8 1 diff --git a/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr b/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr index becd3c8927a..fa6be84c26e 100644 --- a/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr +++ b/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr @@ -1,14 +1,14 @@ // Tests may be checked against https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/main/poc -use dep::std::ec::tecurve::affine::Curve as AffineCurve; -use dep::std::ec::tecurve::affine::Point as Gaffine; -use dep::std::ec::tecurve::curvegroup::Curve; -use dep::std::ec::tecurve::curvegroup::Point as G; +use std::ec::tecurve::affine::Curve as AffineCurve; +use std::ec::tecurve::affine::Point as Gaffine; +use std::ec::tecurve::curvegroup::Curve; +use std::ec::tecurve::curvegroup::Point as G; -use dep::std::ec::swcurve::affine::Point as SWGaffine; -use dep::std::ec::swcurve::curvegroup::Point as SWG; +use std::ec::swcurve::affine::Point as SWGaffine; +use std::ec::swcurve::curvegroup::Point as SWG; -use dep::std::ec::montcurve::affine::Point as MGaffine; -use dep::std::ec::montcurve::curvegroup::Point as MG; +use std::ec::montcurve::affine::Point as MGaffine; +use std::ec::montcurve::curvegroup::Point as MG; fn main() { // This test only makes sense if Field is the right prime field. diff --git a/test_programs/compile_success_empty/intrinsic_die/src/main.nr b/test_programs/compile_success_empty/intrinsic_die/src/main.nr index a6c6d3df9a1..c6e269c155d 100644 --- a/test_programs/compile_success_empty/intrinsic_die/src/main.nr +++ b/test_programs/compile_success_empty/intrinsic_die/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // This test checks that we perform dead-instruction-elimination on intrinsic functions. fn main(x: Field) { let hash = std::hash::pedersen_commitment([x]); diff --git a/test_programs/compile_success_empty/method_call_regression/src/main.nr b/test_programs/compile_success_empty/method_call_regression/src/main.nr index 8bb7ebcac45..88b8dc57196 100644 --- a/test_programs/compile_success_empty/method_call_regression/src/main.nr +++ b/test_programs/compile_success_empty/method_call_regression/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { // s: Struct let s = Struct { b: () }; diff --git a/test_programs/compile_success_empty/overlapping_dep_and_mod/Nargo.toml b/test_programs/compile_success_empty/overlapping_dep_and_mod/Nargo.toml new file mode 100644 index 00000000000..b2c3e5f94be --- /dev/null +++ b/test_programs/compile_success_empty/overlapping_dep_and_mod/Nargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "bin", + "foo", +] +default-member = "bin" diff --git a/test_programs/compile_success_empty/overlapping_dep_and_mod/bin/Nargo.toml b/test_programs/compile_success_empty/overlapping_dep_and_mod/bin/Nargo.toml new file mode 100644 index 00000000000..57e704462db --- /dev/null +++ b/test_programs/compile_success_empty/overlapping_dep_and_mod/bin/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "overlapping_dep_and_mod" +type = "bin" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] +foo = { path = "../foo" } diff --git a/test_programs/compile_success_empty/overlapping_dep_and_mod/bin/src/main.nr b/test_programs/compile_success_empty/overlapping_dep_and_mod/bin/src/main.nr new file mode 100644 index 00000000000..1d9c917d91a --- /dev/null +++ b/test_programs/compile_success_empty/overlapping_dep_and_mod/bin/src/main.nr @@ -0,0 +1,9 @@ +fn main() { + let _ = foo::bar() + dep::foo::baz(); +} + +mod foo { + pub(crate) fn bar() -> Field { + 5 + } +} diff --git a/test_programs/compile_success_empty/overlapping_dep_and_mod/foo/Nargo.toml b/test_programs/compile_success_empty/overlapping_dep_and_mod/foo/Nargo.toml new file mode 100644 index 00000000000..857d4e722a8 --- /dev/null +++ b/test_programs/compile_success_empty/overlapping_dep_and_mod/foo/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "foo" +type = "lib" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/overlapping_dep_and_mod/foo/src/lib.nr b/test_programs/compile_success_empty/overlapping_dep_and_mod/foo/src/lib.nr new file mode 100644 index 00000000000..cb8392ed275 --- /dev/null +++ b/test_programs/compile_success_empty/overlapping_dep_and_mod/foo/src/lib.nr @@ -0,0 +1,5 @@ +// foo/lib.nr + +pub fn baz() -> Field { + 6 +} diff --git a/test_programs/compile_success_empty/reexports/src/main.nr b/test_programs/compile_success_empty/reexports/src/main.nr index ed469ff77d0..0fd65a33564 100644 --- a/test_programs/compile_success_empty/reexports/src/main.nr +++ b/test_programs/compile_success_empty/reexports/src/main.nr @@ -1,4 +1,4 @@ -use dep::reexporting_lib::{FooStruct, MyStruct, lib}; +use reexporting_lib::{FooStruct, MyStruct, lib}; fn main() { let x: FooStruct = MyStruct { inner: 0 }; diff --git a/test_programs/compile_success_empty/regression_2099/src/main.nr b/test_programs/compile_success_empty/regression_2099/src/main.nr index f92373ce63a..660f72f56e5 100644 --- a/test_programs/compile_success_empty/regression_2099/src/main.nr +++ b/test_programs/compile_success_empty/regression_2099/src/main.nr @@ -1,13 +1,13 @@ -use dep::std::ec::tecurve::affine::Curve as AffineCurve; -use dep::std::ec::tecurve::affine::Point as Gaffine; -use dep::std::ec::tecurve::curvegroup::Curve; -use dep::std::ec::tecurve::curvegroup::Point as G; +use std::ec::tecurve::affine::Curve as AffineCurve; +use std::ec::tecurve::affine::Point as Gaffine; +use std::ec::tecurve::curvegroup::Curve; +use std::ec::tecurve::curvegroup::Point as G; -use dep::std::ec::swcurve::affine::Point as SWGaffine; -use dep::std::ec::swcurve::curvegroup::Point as SWG; +use std::ec::swcurve::affine::Point as SWGaffine; +use std::ec::swcurve::curvegroup::Point as SWG; -use dep::std::ec::montcurve::affine::Point as MGaffine; -use dep::std::ec::montcurve::curvegroup::Point as MG; +use std::ec::montcurve::affine::Point as MGaffine; +use std::ec::montcurve::curvegroup::Point as MG; fn main() { // Define Baby Jubjub (ERC-2494) parameters in affine representation diff --git a/test_programs/compile_success_empty/regression_3635/src/main.nr b/test_programs/compile_success_empty/regression_3635/src/main.nr index 97a04f9d93f..edc6d8690e8 100644 --- a/test_programs/compile_success_empty/regression_3635/src/main.nr +++ b/test_programs/compile_success_empty/regression_3635/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { let x: u8 = 0x61; let y: u8 = "a".as_bytes()[0]; diff --git a/test_programs/compile_success_empty/str_as_bytes/src/main.nr b/test_programs/compile_success_empty/str_as_bytes/src/main.nr index 6fdd926ce7f..1330924e501 100644 --- a/test_programs/compile_success_empty/str_as_bytes/src/main.nr +++ b/test_programs/compile_success_empty/str_as_bytes/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; fn main() { let a = "hello"; let b = a.as_bytes(); diff --git a/test_programs/compile_success_empty/trait_default_implementation/src/main.nr b/test_programs/compile_success_empty/trait_default_implementation/src/main.nr index 2f5bff8c40c..90c375b6010 100644 --- a/test_programs/compile_success_empty/trait_default_implementation/src/main.nr +++ b/test_programs/compile_success_empty/trait_default_implementation/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - trait MyDefault { fn my_default(x: Field, y: Field) -> Self; diff --git a/test_programs/compile_success_empty/trait_override_implementation/src/main.nr b/test_programs/compile_success_empty/trait_override_implementation/src/main.nr index 85528291870..21d89b1b261 100644 --- a/test_programs/compile_success_empty/trait_override_implementation/src/main.nr +++ b/test_programs/compile_success_empty/trait_override_implementation/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - trait MyDefault { fn my_default(x: Field, y: Field) -> Self; diff --git a/test_programs/compile_success_empty/traits/src/main.nr b/test_programs/compile_success_empty/traits/src/main.nr index ed804559fed..0a5644e7530 100644 --- a/test_programs/compile_success_empty/traits/src/main.nr +++ b/test_programs/compile_success_empty/traits/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - trait MyDefault { fn my_default(x: Field, y: Field) -> Self; } diff --git a/test_programs/compile_success_empty/vectors/src/main.nr b/test_programs/compile_success_empty/vectors/src/main.nr index d105ceed180..ac02a4691dd 100644 --- a/test_programs/compile_success_empty/vectors/src/main.nr +++ b/test_programs/compile_success_empty/vectors/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::collections::vec::Vec; +use std::collections::vec::Vec; fn main(x: Field, y: pub Field) { let mut vector = Vec::new(); diff --git a/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr b/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr index ab0ae9a48b8..a4207794a8a 100644 --- a/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr +++ b/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr @@ -1,2 +1,2 @@ -use dep::library::ReExportMeFromAnotherLib; +use library::ReExportMeFromAnotherLib; fn main(_x: ReExportMeFromAnotherLib) {} diff --git a/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr b/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr index 8e84662ed03..e3a1539ea65 100644 --- a/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr +++ b/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr @@ -1,2 +1,2 @@ // Re-export -use dep::library2::ReExportMeFromAnotherLib; +use library2::ReExportMeFromAnotherLib; diff --git a/test_programs/execution_failure/div_by_zero_constants/src/main.nr b/test_programs/execution_failure/div_by_zero_constants/src/main.nr index 58adc5444b1..f90b3ef9429 100644 --- a/test_programs/execution_failure/div_by_zero_constants/src/main.nr +++ b/test_programs/execution_failure/div_by_zero_constants/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { let a: Field = 3 / 0; std::println(a); diff --git a/test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr b/test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr index f51b26d5ba1..012e823b297 100644 --- a/test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr +++ b/test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(x: Field) { let a: Field = x / 0; std::println(a); diff --git a/test_programs/execution_failure/div_by_zero_witness/src/main.nr b/test_programs/execution_failure/div_by_zero_witness/src/main.nr index a814f88f320..eaa3c1f2a72 100644 --- a/test_programs/execution_failure/div_by_zero_witness/src/main.nr +++ b/test_programs/execution_failure/div_by_zero_witness/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // It is expected that `y` must be equal to 0. fn main(x: Field, y: pub Field) { let a: Field = x / y; diff --git a/test_programs/execution_failure/hashmap_load_factor/src/main.nr b/test_programs/execution_failure/hashmap_load_factor/src/main.nr index 907c3628142..e95da67a084 100644 --- a/test_programs/execution_failure/hashmap_load_factor/src/main.nr +++ b/test_programs/execution_failure/hashmap_load_factor/src/main.nr @@ -1,6 +1,6 @@ -use dep::std::collections::map::HashMap; -use dep::std::hash::BuildHasherDefault; -use dep::std::hash::poseidon2::Poseidon2Hasher; +use std::collections::map::HashMap; +use std::hash::BuildHasherDefault; +use std::hash::poseidon2::Poseidon2Hasher; struct Entry{ key: Field, diff --git a/test_programs/execution_success/4_sub/src/main.nr b/test_programs/execution_success/4_sub/src/main.nr index 6aef8e7b208..2b4fc395705 100644 --- a/test_programs/execution_success/4_sub/src/main.nr +++ b/test_programs/execution_success/4_sub/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Test unsafe integer subtraction with underflow: 12 - 2418266113 = 1876701195 modulo 2^32 fn main(mut x: u32, y: u32, z: u32) { x = std::wrapping_sub(x,y); diff --git a/test_programs/execution_success/5_over/src/main.nr b/test_programs/execution_success/5_over/src/main.nr index 313d580a8d1..6c4153e4b49 100644 --- a/test_programs/execution_success/5_over/src/main.nr +++ b/test_programs/execution_success/5_over/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Test unsafe integer arithmetic // Test odd bits integer fn main(mut x: u32, y: u32) { diff --git a/test_programs/execution_success/6/src/main.nr b/test_programs/execution_success/6/src/main.nr index 5ecb809e68b..8657199bd7f 100644 --- a/test_programs/execution_success/6/src/main.nr +++ b/test_programs/execution_success/6/src/main.nr @@ -5,7 +5,6 @@ // If you do not cast, it will take all the bytes from the field element! // Mimc input is an array of field elements // The function is called mimc_bn254 to emphasize its parameters are chosen for bn254 curve, it should be used only with a proving system using the same curve (e.g Plonk from Aztec) -use dep::std; fn main(x: [u8; 5], result: pub [u8; 32]) { let mut digest = std::hash::sha256(x); diff --git a/test_programs/execution_success/6_array/src/main.nr b/test_programs/execution_success/6_array/src/main.nr index 6aa05f58b71..d7180c260ff 100644 --- a/test_programs/execution_success/6_array/src/main.nr +++ b/test_programs/execution_success/6_array/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; //Basic tests for arrays fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) { let mut c = 2301; diff --git a/test_programs/execution_success/7/src/main.nr b/test_programs/execution_success/7/src/main.nr index a6bba978644..ad3fe1aadc8 100644 --- a/test_programs/execution_success/7/src/main.nr +++ b/test_programs/execution_success/7/src/main.nr @@ -2,7 +2,6 @@ // // Pre-alpha dependencies must now be prefixed with the word "dep". // The line below indicates that we would like to pull in the standard library dependency. -use dep::std; fn main(x: [u8; 5], result: [u8; 32]) { let digest = std::hash::blake2s(x); diff --git a/test_programs/execution_success/aes128_encrypt/src/main.nr b/test_programs/execution_success/aes128_encrypt/src/main.nr index f6ed0f309c3..cd7fb4772e2 100644 --- a/test_programs/execution_success/aes128_encrypt/src/main.nr +++ b/test_programs/execution_success/aes128_encrypt/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - unconstrained fn decode_ascii(ascii: u8) -> u8 { if ascii < 58 { ascii - 48 diff --git a/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr b/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr index 4cbf1bd8e6d..260d609928b 100644 --- a/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr +++ b/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr @@ -18,7 +18,7 @@ fn compute_root(leaf: [u8; 32], path: [u8; 64], _index: u32, root: [u8; 32]) { hash_input[j + b] = path[offset + j]; } - current = dep::std::hash::sha256(hash_input); + current = std::hash::sha256(hash_input); index = index >> 1; } diff --git a/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr b/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr index 8faaf69dfc8..15a2747eaa9 100644 --- a/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr +++ b/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr @@ -15,6 +15,6 @@ fn main(mut x: [Foo; 3], y: pub Field, hash_result: pub [u8; 32]) { // Make sure that we are passing a dynamic array to the black box function call // by setting the array using a dynamic index here hash_input[y - 1] = 0; - let hash = dep::std::hash::sha256(hash_input); + let hash = std::hash::sha256(hash_input); assert_eq(hash, hash_result); } diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr index c454c2b66cd..9385b39e847 100644 --- a/test_programs/execution_success/bigint/src/main.nr +++ b/test_programs/execution_success/bigint/src/main.nr @@ -1,5 +1,5 @@ -use dep::std::bigint; -use dep::std::{bigint::Secpk1Fq, println}; +use std::bigint; +use std::{bigint::Secpk1Fq, println}; fn main(mut x: [u8; 5], y: [u8; 5]) { let a = bigint::Secpk1Fq::from_le_bytes(&[x[0], x[1], x[2], x[3], x[4]]); @@ -10,8 +10,8 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { a_be_bytes[31-i] = x[i]; b_be_bytes[31-i] = y[i]; } - let a_field = dep::std::field::bytes32_to_field(a_be_bytes); - let b_field = dep::std::field::bytes32_to_field(b_be_bytes); + let a_field = std::field::bytes32_to_field(a_be_bytes); + let b_field = std::field::bytes32_to_field(b_be_bytes); // Regression for issue #4682 let c = if x[0] != 0 { @@ -19,7 +19,7 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { } else { test_unconstrained2(a, b) }; - assert(c.array[0] == dep::std::wrapping_mul(x[0], y[0])); + assert(c.array[0] == std::wrapping_mul(x[0], y[0])); let a_bytes = a.to_le_bytes(); let b_bytes = b.to_le_bytes(); diff --git a/test_programs/execution_success/blake3/src/main.nr b/test_programs/execution_success/blake3/src/main.nr index 3bfea6c5f95..fb056bfa848 100644 --- a/test_programs/execution_success/blake3/src/main.nr +++ b/test_programs/execution_success/blake3/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(x: [u8; 5], result: [u8; 32]) { let digest = std::hash::blake3(x); assert(digest == result); diff --git a/test_programs/execution_success/brillig_blake2s/src/main.nr b/test_programs/execution_success/brillig_blake2s/src/main.nr index 5bd52666ae9..2743e02e920 100644 --- a/test_programs/execution_success/brillig_blake2s/src/main.nr +++ b/test_programs/execution_success/brillig_blake2s/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Tests a very simple program. // // The features being tested is blake2s in brillig diff --git a/test_programs/execution_success/brillig_blake3/src/main.nr b/test_programs/execution_success/brillig_blake3/src/main.nr index 05a5b31f936..64852d775f4 100644 --- a/test_programs/execution_success/brillig_blake3/src/main.nr +++ b/test_programs/execution_success/brillig_blake3/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - unconstrained fn main(x: [u8; 5], result: [u8; 32]) { let digest = std::hash::blake3(x); assert(digest == result); diff --git a/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr b/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr index 142290ecbe2..2809668c574 100644 --- a/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr +++ b/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr @@ -38,11 +38,11 @@ struct Outer { // If we don't take into account block parameter liveness, this function will need 5*500=2500 stack items unconstrained fn main(conditions: [bool; 5]) -> pub Outer { let out0 = if conditions[0] { - let mut outer: Outer = dep::std::unsafe::zeroed(); + let mut outer: Outer = std::unsafe::zeroed(); outer.middle_a.inner_a.a = 1; outer } else { - let mut outer: Outer= dep::std::unsafe::zeroed(); + let mut outer: Outer= std::unsafe::zeroed(); outer.middle_f.inner_c.d = 2; outer }; diff --git a/test_programs/execution_success/brillig_cow_regression/src/main.nr b/test_programs/execution_success/brillig_cow_regression/src/main.nr index 1cae9b1ba41..7cd50860978 100644 --- a/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -25,7 +25,7 @@ struct NewContractData { impl NewContractData { fn hash(self) -> Field { - dep::std::hash::pedersen_hash([self.contract_address, self.portal_contract_address]) + std::hash::pedersen_hash([self.contract_address, self.portal_contract_address]) } } @@ -173,6 +173,6 @@ unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA } } - let sha_digest = dep::std::hash::sha256(hash_input_flattened); + let sha_digest = std::hash::sha256(hash_input_flattened); U256::from_bytes32(sha_digest).to_u128_limbs() } diff --git a/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr b/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr index 5d84d885567..78343dcd26c 100644 --- a/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr +++ b/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Tests a very simple program. // // The features being tested is ecdsa in brillig diff --git a/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr b/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr index 9da07f531aa..48debadb012 100644 --- a/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr +++ b/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Tests a very simple program. // // The features being tested is ecdsa in brillig diff --git a/test_programs/execution_success/brillig_fns_as_values/src/main.nr b/test_programs/execution_success/brillig_fns_as_values/src/main.nr index 9248bff2f4c..1476c447431 100644 --- a/test_programs/execution_success/brillig_fns_as_values/src/main.nr +++ b/test_programs/execution_success/brillig_fns_as_values/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - struct MyStruct { operation: fn (u32) -> u32, } diff --git a/test_programs/execution_success/brillig_hash_to_field/src/main.nr b/test_programs/execution_success/brillig_hash_to_field/src/main.nr index 53ed85b3ddd..78759bd84c6 100644 --- a/test_programs/execution_success/brillig_hash_to_field/src/main.nr +++ b/test_programs/execution_success/brillig_hash_to_field/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Tests a very simple program. // // The features being tested is hash_to_field in brillig diff --git a/test_programs/execution_success/brillig_keccak/src/main.nr b/test_programs/execution_success/brillig_keccak/src/main.nr index a300bc18279..9150e38f208 100644 --- a/test_programs/execution_success/brillig_keccak/src/main.nr +++ b/test_programs/execution_success/brillig_keccak/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Tests a very simple program. // // The features being tested is keccak256 in brillig diff --git a/test_programs/execution_success/brillig_oracle/src/main.nr b/test_programs/execution_success/brillig_oracle/src/main.nr index 6a9e5806621..0305cb06978 100644 --- a/test_programs/execution_success/brillig_oracle/src/main.nr +++ b/test_programs/execution_success/brillig_oracle/src/main.nr @@ -1,5 +1,5 @@ -use dep::std::slice; -use dep::std::test::OracleMock; +use std::slice; +use std::test::OracleMock; // Tests oracle usage in brillig/unconstrained functions fn main(_x: Field) { diff --git a/test_programs/execution_success/brillig_pedersen/src/main.nr b/test_programs/execution_success/brillig_pedersen/src/main.nr index 2379818c454..17c79f9d0ae 100644 --- a/test_programs/execution_success/brillig_pedersen/src/main.nr +++ b/test_programs/execution_success/brillig_pedersen/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - unconstrained fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { let res = std::hash::pedersen_commitment_with_separator([x, y], 0); assert(res.x == out_x); diff --git a/test_programs/execution_success/brillig_sha256/src/main.nr b/test_programs/execution_success/brillig_sha256/src/main.nr index e76109df9c3..fcc01978a0a 100644 --- a/test_programs/execution_success/brillig_sha256/src/main.nr +++ b/test_programs/execution_success/brillig_sha256/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Tests a very simple program. // // The features being tested is sha256 in brillig diff --git a/test_programs/execution_success/brillig_slices/src/main.nr b/test_programs/execution_success/brillig_slices/src/main.nr index 2cf1850f151..89f838a3a57 100644 --- a/test_programs/execution_success/brillig_slices/src/main.nr +++ b/test_programs/execution_success/brillig_slices/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::slice; +use std::slice; unconstrained fn main(x: Field, y: Field) { let mut slice: [Field] = &[y, x]; assert(slice.len() == 2); diff --git a/test_programs/execution_success/conditional_1/src/main.nr b/test_programs/execution_success/conditional_1/src/main.nr index 5064c82bce9..e7d780263b8 100644 --- a/test_programs/execution_success/conditional_1/src/main.nr +++ b/test_programs/execution_success/conditional_1/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn sort(mut a: [u32; 4]) -> [u32; 4] { for i in 1..4 { for j in 0..i { diff --git a/test_programs/execution_success/conditional_2/src/main.nr b/test_programs/execution_success/conditional_2/src/main.nr index 5b3f64f6be5..ea23ec3cf3b 100644 --- a/test_programs/execution_success/conditional_2/src/main.nr +++ b/test_programs/execution_success/conditional_2/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn must_be_zero(x: u8) { assert(x == 0); } diff --git a/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr b/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr index d260fa49dc3..de5ad20a642 100644 --- a/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr +++ b/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { //regression for short-circuit2 if 35 == a { diff --git a/test_programs/execution_success/databus/src/main.nr b/test_programs/execution_success/databus/src/main.nr index 1cf95be8a22..7e5c23d508d 100644 --- a/test_programs/execution_success/databus/src/main.nr +++ b/test_programs/execution_success/databus/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(mut x: u32, y: call_data u32, z: call_data [u32; 4]) -> return_data u32 { let a = z[x]; a + foo(y) diff --git a/test_programs/execution_success/diamond_deps_0/src/main.nr b/test_programs/execution_success/diamond_deps_0/src/main.nr index ca95c6e0aa8..690d6fc9fc8 100644 --- a/test_programs/execution_success/diamond_deps_0/src/main.nr +++ b/test_programs/execution_success/diamond_deps_0/src/main.nr @@ -1,6 +1,6 @@ -use dep::dep1::call_dep1_then_dep2; -use dep::dep2::call_dep2; -use dep::dep2::RESOLVE_THIS; +use dep1::call_dep1_then_dep2; +use dep2::call_dep2; +use dep2::RESOLVE_THIS; fn main(x: Field, y: pub Field) -> pub Field { call_dep1_then_dep2(x, y) + call_dep2(x, y) + RESOLVE_THIS diff --git a/test_programs/execution_success/double_verify_nested_proof/src/main.nr b/test_programs/execution_success/double_verify_nested_proof/src/main.nr index 95d4b6f6995..5f0eb1a5b53 100644 --- a/test_programs/execution_success/double_verify_nested_proof/src/main.nr +++ b/test_programs/execution_success/double_verify_nested_proof/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // This circuit aggregates two recursive proofs from `double_verify_proof_recursive`. // Recursive aggregation is a backend-specific process and it is expected for backends diff --git a/test_programs/execution_success/double_verify_proof/src/main.nr b/test_programs/execution_success/double_verify_proof/src/main.nr index d832ce0f049..d3b909c3fa4 100644 --- a/test_programs/execution_success/double_verify_proof/src/main.nr +++ b/test_programs/execution_success/double_verify_proof/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // This circuit aggregates two proofs from `assert_statement_recursive`. fn main( diff --git a/test_programs/execution_success/double_verify_proof_recursive/src/main.nr b/test_programs/execution_success/double_verify_proof_recursive/src/main.nr index 86b4971c3a6..2555bbc4758 100644 --- a/test_programs/execution_success/double_verify_proof_recursive/src/main.nr +++ b/test_programs/execution_success/double_verify_proof_recursive/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // This circuit aggregates two proofs from `assert_statement_recursive`. #[recursive] diff --git a/test_programs/execution_success/ecdsa_secp256k1/src/main.nr b/test_programs/execution_success/ecdsa_secp256k1/src/main.nr index ac0359e4bb8..7f0999fc360 100644 --- a/test_programs/execution_success/ecdsa_secp256k1/src/main.nr +++ b/test_programs/execution_success/ecdsa_secp256k1/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main( message: [u8; 38], hashed_message: [u8; 32], diff --git a/test_programs/execution_success/ecdsa_secp256r1/src/main.nr b/test_programs/execution_success/ecdsa_secp256r1/src/main.nr index c64e390d652..09f427c37d9 100644 --- a/test_programs/execution_success/ecdsa_secp256r1/src/main.nr +++ b/test_programs/execution_success/ecdsa_secp256r1/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); assert(valid_signature); diff --git a/test_programs/execution_success/ecdsa_secp256r1_3x/src/main.nr b/test_programs/execution_success/ecdsa_secp256r1_3x/src/main.nr index e7a6be9d47a..b5bd633915f 100644 --- a/test_programs/execution_success/ecdsa_secp256r1_3x/src/main.nr +++ b/test_programs/execution_success/ecdsa_secp256r1_3x/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main( hashed_message: [u8; 32], pub_key_x: [u8; 32], diff --git a/test_programs/execution_success/eddsa/src/main.nr b/test_programs/execution_success/eddsa/src/main.nr index ada15d5405c..407ca049806 100644 --- a/test_programs/execution_success/eddsa/src/main.nr +++ b/test_programs/execution_success/eddsa/src/main.nr @@ -1,9 +1,9 @@ -use dep::std::compat; -use dep::std::ec::consts::te::baby_jubjub; -use dep::std::ec::tecurve::affine::Point as TEPoint; -use dep::std::hash; -use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify, eddsa_verify}; -use dep::std::hash::poseidon2::Poseidon2Hasher; +use std::compat; +use std::ec::consts::te::baby_jubjub; +use std::ec::tecurve::affine::Point as TEPoint; +use std::hash; +use std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify, eddsa_verify}; +use std::hash::poseidon2::Poseidon2Hasher; fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // Skip this test for non-bn254 backends diff --git a/test_programs/execution_success/embedded_curve_ops/src/main.nr b/test_programs/execution_success/embedded_curve_ops/src/main.nr index 46f919e947a..4eeda39c6aa 100644 --- a/test_programs/execution_success/embedded_curve_ops/src/main.nr +++ b/test_programs/execution_success/embedded_curve_ops/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(priv_key: Field, pub_x: pub Field, pub_y: pub Field) { let g1_y = 17631683881184975370165255887551781615748388533673675138860; let g1 = std::embedded_curve_ops::EmbeddedCurvePoint { x: 1, y: g1_y, is_infinite: false }; diff --git a/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr b/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr index f9f3e75789b..7d12d63634b 100644 --- a/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr +++ b/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; +use std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; global NUM_HASHES = 2; global HASH_LENGTH = 10; diff --git a/test_programs/execution_success/generics/src/main.nr b/test_programs/execution_success/generics/src/main.nr index c8616960559..f754fb96292 100644 --- a/test_programs/execution_success/generics/src/main.nr +++ b/test_programs/execution_success/generics/src/main.nr @@ -34,7 +34,7 @@ impl Bar { impl Bar { // This is to test that we can use turbofish on methods as well fn zeroed(_self: Self) -> A { - dep::std::unsafe::zeroed() + std::unsafe::zeroed() } } diff --git a/test_programs/execution_success/hash_to_field/src/main.nr b/test_programs/execution_success/hash_to_field/src/main.nr index 242b5ecbc18..bb4f829ec33 100644 --- a/test_programs/execution_success/hash_to_field/src/main.nr +++ b/test_programs/execution_success/hash_to_field/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(input: Field) -> pub Field { std::hash::hash_to_field(&[input]) } diff --git a/test_programs/execution_success/hashmap/src/main.nr b/test_programs/execution_success/hashmap/src/main.nr index 76daa594a89..8cf70cc5970 100644 --- a/test_programs/execution_success/hashmap/src/main.nr +++ b/test_programs/execution_success/hashmap/src/main.nr @@ -1,9 +1,9 @@ mod utils; -use dep::std::collections::map::HashMap; -use dep::std::hash::BuildHasherDefault; -use dep::std::hash::poseidon2::Poseidon2Hasher; -use dep::std::cmp::Eq; +use std::collections::map::HashMap; +use std::hash::BuildHasherDefault; +use std::hash::poseidon2::Poseidon2Hasher; +use std::cmp::Eq; use utils::cut; diff --git a/test_programs/execution_success/import/src/main.nr b/test_programs/execution_success/import/src/main.nr index 7dcc16fed16..0f5aa7e5460 100644 --- a/test_programs/execution_success/import/src/main.nr +++ b/test_programs/execution_success/import/src/main.nr @@ -2,7 +2,7 @@ mod import; use crate::import::hello; fn main(x: Field, y: Field) { - let _k = dep::std::hash::pedersen_commitment([x]); + let _k = std::hash::pedersen_commitment([x]); let _l = hello(x); assert(x != import::hello(y)); diff --git a/test_programs/execution_success/is_unconstrained/src/main.nr b/test_programs/execution_success/is_unconstrained/src/main.nr index af40af1029f..ddafc73c598 100644 --- a/test_programs/execution_success/is_unconstrained/src/main.nr +++ b/test_programs/execution_success/is_unconstrained/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::runtime::is_unconstrained; +use std::runtime::is_unconstrained; fn check(should_be_unconstrained: bool) { assert_eq(should_be_unconstrained, is_unconstrained()); diff --git a/test_programs/execution_success/keccak256/src/main.nr b/test_programs/execution_success/keccak256/src/main.nr index eb401fe614c..ff18cae0c9c 100644 --- a/test_programs/execution_success/keccak256/src/main.nr +++ b/test_programs/execution_success/keccak256/src/main.nr @@ -1,5 +1,4 @@ // docs:start:keccak256 -use dep::std; fn main(x: Field, result: [u8; 32]) { // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field diff --git a/test_programs/execution_success/merkle_insert/src/main.nr b/test_programs/execution_success/merkle_insert/src/main.nr index ac9a7b34ea3..a08088e847b 100644 --- a/test_programs/execution_success/merkle_insert/src/main.nr +++ b/test_programs/execution_success/merkle_insert/src/main.nr @@ -1,5 +1,4 @@ -use dep::std; -use dep::std::hash::mimc; +use std::hash::mimc; fn main( old_root: Field, diff --git a/test_programs/execution_success/modulus/src/main.nr b/test_programs/execution_success/modulus/src/main.nr index 35f63fdc8c5..c7d6a2e2c7d 100644 --- a/test_programs/execution_success/modulus/src/main.nr +++ b/test_programs/execution_success/modulus/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(bn254_modulus_be_bytes: [u8; 32], bn254_modulus_be_bits: [u1; 254]) { 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 diff --git a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr index dcb5e6bc5ca..94db8c2b414 100644 --- a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr +++ b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; +use std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; global NUM_HASHES = 2; global HASH_LENGTH = 10; diff --git a/test_programs/execution_success/operator_overloading/src/main.nr b/test_programs/execution_success/operator_overloading/src/main.nr index 3956ea5c577..c2c831d0c1e 100644 --- a/test_programs/execution_success/operator_overloading/src/main.nr +++ b/test_programs/execution_success/operator_overloading/src/main.nr @@ -1,5 +1,5 @@ -use dep::std::ops::{Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shl, Shr}; -use dep::std::cmp::Ordering; +use std::ops::{Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shl, Shr}; +use std::cmp::Ordering; // x = 3, y = 9 fn main(x: u32, y: u32) { diff --git a/test_programs/execution_success/overlapping_dep_and_mod/Nargo.toml b/test_programs/execution_success/overlapping_dep_and_mod/Nargo.toml new file mode 100644 index 00000000000..b2c3e5f94be --- /dev/null +++ b/test_programs/execution_success/overlapping_dep_and_mod/Nargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "bin", + "foo", +] +default-member = "bin" diff --git a/test_programs/execution_success/overlapping_dep_and_mod/bin/Nargo.toml b/test_programs/execution_success/overlapping_dep_and_mod/bin/Nargo.toml new file mode 100644 index 00000000000..57e704462db --- /dev/null +++ b/test_programs/execution_success/overlapping_dep_and_mod/bin/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "overlapping_dep_and_mod" +type = "bin" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] +foo = { path = "../foo" } diff --git a/test_programs/execution_success/overlapping_dep_and_mod/bin/Prover.toml b/test_programs/execution_success/overlapping_dep_and_mod/bin/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/execution_success/overlapping_dep_and_mod/bin/src/main.nr b/test_programs/execution_success/overlapping_dep_and_mod/bin/src/main.nr new file mode 100644 index 00000000000..16c940f12fc --- /dev/null +++ b/test_programs/execution_success/overlapping_dep_and_mod/bin/src/main.nr @@ -0,0 +1,14 @@ +// bin/main.nr + +fn main() -> pub Field { + assert(foo::bar() + dep::foo::bar() == 11); + assert(foo::bar() == 5); + assert(dep::foo::bar() == 6); + foo::bar() + dep::foo::bar() +} + +mod foo { + pub(crate) fn bar() -> Field { + 5 + } +} diff --git a/test_programs/execution_success/overlapping_dep_and_mod/foo/Nargo.toml b/test_programs/execution_success/overlapping_dep_and_mod/foo/Nargo.toml new file mode 100644 index 00000000000..857d4e722a8 --- /dev/null +++ b/test_programs/execution_success/overlapping_dep_and_mod/foo/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "foo" +type = "lib" +authors = [""] +compiler_version = ">=0.29.0" + +[dependencies] diff --git a/test_programs/execution_success/overlapping_dep_and_mod/foo/src/lib.nr b/test_programs/execution_success/overlapping_dep_and_mod/foo/src/lib.nr new file mode 100644 index 00000000000..09283defed7 --- /dev/null +++ b/test_programs/execution_success/overlapping_dep_and_mod/foo/src/lib.nr @@ -0,0 +1,5 @@ +// foo/lib.nr + +pub fn bar() -> Field { + 6 +} diff --git a/test_programs/execution_success/pedersen_check/src/main.nr b/test_programs/execution_success/pedersen_check/src/main.nr index 90ef218249b..c71b2b570da 100644 --- a/test_programs/execution_success/pedersen_check/src/main.nr +++ b/test_programs/execution_success/pedersen_check/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { let res = std::hash::pedersen_commitment([x, y]); assert(res.x == out_x); diff --git a/test_programs/execution_success/pedersen_commitment/src/main.nr b/test_programs/execution_success/pedersen_commitment/src/main.nr index a3a9aea1cf0..81c605a64dd 100644 --- a/test_programs/execution_success/pedersen_commitment/src/main.nr +++ b/test_programs/execution_success/pedersen_commitment/src/main.nr @@ -1,5 +1,4 @@ // docs:start:pedersen-commitment -use dep::std; fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { let commitment = std::hash::pedersen_commitment([x, y]); diff --git a/test_programs/execution_success/pedersen_hash/src/main.nr b/test_programs/execution_success/pedersen_hash/src/main.nr index 20c7de12d6c..f8ec56a9d98 100644 --- a/test_programs/execution_success/pedersen_hash/src/main.nr +++ b/test_programs/execution_success/pedersen_hash/src/main.nr @@ -1,5 +1,4 @@ // docs:start:pedersen-hash -use dep::std; fn main(x: Field, y: Field, expected_hash: Field) { let hash = std::hash::pedersen_hash([x, y]); diff --git a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr index a1607956190..cf1c190e5c9 100644 --- a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr +++ b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr @@ -1,6 +1,6 @@ // docs:start:poseidon -use dep::std::hash::poseidon; -use dep::std::hash::poseidon2; +use std::hash::poseidon; +use std::hash::poseidon2; fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field; 4], y3: Field) { let hash1 = poseidon::bn254::hash_2(x1); diff --git a/test_programs/execution_success/poseidonsponge_x5_254/src/main.nr b/test_programs/execution_success/poseidonsponge_x5_254/src/main.nr index 910a17c8c89..137f3e5d2a6 100644 --- a/test_programs/execution_success/poseidonsponge_x5_254/src/main.nr +++ b/test_programs/execution_success/poseidonsponge_x5_254/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash::poseidon; +use std::hash::poseidon; fn main(x: [Field; 7]) { // Test optimized sponge diff --git a/test_programs/execution_success/prelude/src/main.nr b/test_programs/execution_success/prelude/src/main.nr index 226341f1e7b..4fe6080222e 100644 --- a/test_programs/execution_success/prelude/src/main.nr +++ b/test_programs/execution_success/prelude/src/main.nr @@ -8,8 +8,8 @@ fn main() { mod a { // We don't want to give an error due to re-importing elements that are already in the prelude. - use dep::std::collections::vec::Vec; - use dep::std::option::Option; + use std::collections::vec::Vec; + use std::option::Option; fn main() { let _xs = Vec::new(); diff --git a/test_programs/execution_success/regression_3051/src/main.nr b/test_programs/execution_success/regression_3051/src/main.nr index 2e7d10fd7b0..90eb652db7a 100644 --- a/test_programs/execution_success/regression_3051/src/main.nr +++ b/test_programs/execution_success/regression_3051/src/main.nr @@ -19,6 +19,6 @@ impl Bar for u64 { } fn main() { - dep::std::println(1.foo()); - dep::std::println(1.bar()); + std::println(1.foo()); + std::println(1.bar()); } diff --git a/test_programs/execution_success/regression_3394/src/main.nr b/test_programs/execution_success/regression_3394/src/main.nr index 94b6c818ff2..59494253757 100644 --- a/test_programs/execution_success/regression_3394/src/main.nr +++ b/test_programs/execution_success/regression_3394/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { let x : i8 = -128; std::println(x); diff --git a/test_programs/execution_success/regression_4124/src/main.nr b/test_programs/execution_success/regression_4124/src/main.nr index 49ff68ee6ad..2b0e65a0b6c 100644 --- a/test_programs/execution_success/regression_4124/src/main.nr +++ b/test_programs/execution_success/regression_4124/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::option::Option; +use std::option::Option; trait MyDeserialize { fn deserialize(fields: [Field; N]) -> Self; @@ -11,7 +11,7 @@ impl MyDeserialize<1> for Field { } pub fn storage_read() -> [Field; N] { - dep::std::unsafe::zeroed() + std::unsafe::zeroed() } struct PublicMutable { diff --git a/test_programs/execution_success/regression_4449/src/main.nr b/test_programs/execution_success/regression_4449/src/main.nr index 454a93f5d1a..66bab2e09f4 100644 --- a/test_programs/execution_success/regression_4449/src/main.nr +++ b/test_programs/execution_success/regression_4449/src/main.nr @@ -1,5 +1,4 @@ // Regression test for issue #4449 -use dep::std; fn main(x: u8, result: [u8; 32]) { let x = x % 31; diff --git a/test_programs/execution_success/regression_5045/src/main.nr b/test_programs/execution_success/regression_5045/src/main.nr index 015fb1b2555..cf39b2f97e4 100644 --- a/test_programs/execution_success/regression_5045/src/main.nr +++ b/test_programs/execution_success/regression_5045/src/main.nr @@ -1,5 +1,5 @@ -use dep::std::embedded_curve_ops::EmbeddedCurvePoint; -use dep::std::embedded_curve_ops::EmbeddedCurveScalar; +use std::embedded_curve_ops::EmbeddedCurvePoint; +use std::embedded_curve_ops::EmbeddedCurveScalar; fn main(is_active: bool) { let a = EmbeddedCurvePoint { @@ -11,7 +11,7 @@ fn main(is_active: bool) { if is_active { let bad = EmbeddedCurvePoint { x: 0, y: 5, is_infinite: false }; let d = bad.double(); - let e = dep::std::embedded_curve_ops::multi_scalar_mul( + let e = std::embedded_curve_ops::multi_scalar_mul( [a, bad], [EmbeddedCurveScalar { lo: 1, hi: 0 }, EmbeddedCurveScalar { lo: 1, hi: 0 }] ); diff --git a/test_programs/execution_success/regression_5202/src/main.nr b/test_programs/execution_success/regression_5202/src/main.nr index e41b760b83e..2ebfd7890ba 100644 --- a/test_programs/execution_success/regression_5202/src/main.nr +++ b/test_programs/execution_success/regression_5202/src/main.nr @@ -1,4 +1,4 @@ -use dep::fraction::{Fraction, MAX, floor, toFraction, addFraction}; +use fraction::{Fraction, MAX, floor, toFraction, addFraction}; fn main() { let g1 = toFraction(true, 33333333, 5); diff --git a/test_programs/execution_success/regression_5252/src/main.nr b/test_programs/execution_success/regression_5252/src/main.nr index 315807c3396..0bfd596a777 100644 --- a/test_programs/execution_success/regression_5252/src/main.nr +++ b/test_programs/execution_success/regression_5252/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::hash::{mimc, poseidon, poseidon2::Poseidon2}; +use std::hash::{mimc, poseidon, poseidon2::Poseidon2}; global NUM_HASHES = 3; global HASH_LENGTH = 20; diff --git a/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr b/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr index 1a6931a6870..c7db543f1bb 100644 --- a/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr +++ b/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; struct Item { id: Field, } diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr index 1067d9707b2..5bc0ca9fefb 100644 --- a/test_programs/execution_success/schnorr/src/main.nr +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -1,5 +1,4 @@ -use dep::std; -use dep::std::embedded_curve_ops; +use std::embedded_curve_ops; // Note: If main has any unsized types, then the verifier will never be able // to figure out the circuit instance @@ -37,11 +36,7 @@ fn main( // Meanwhile, you have to use a message with 32 additional bytes: // If you want to verify a signature on a message of 10 bytes, you need to pass a message of length 42, // where the first 10 bytes are the one from the original message (the other bytes are not used) -pub fn verify_signature_noir( - public_key: embedded_curve_ops::EmbeddedCurvePoint, - signature: [u8; 64], - message: [u8; M] -) -> bool { +pub fn verify_signature_noir(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) -> bool { let N = message.len() - 32; //scalar lo/hi from bytes @@ -90,11 +85,7 @@ pub fn bytes_to_scalar(bytes: [u8; 64], offset: u32) -> embedded_curve_ops::Embe sig_s } -pub fn assert_valid_signature( - public_key: embedded_curve_ops::EmbeddedCurvePoint, - signature: [u8; 64], - message: [u8; M] -) { +pub fn assert_valid_signature(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) { let N = message.len() - 32; //scalar lo/hi from bytes let sig_s = bytes_to_scalar(signature, 0); diff --git a/test_programs/execution_success/sha256/src/main.nr b/test_programs/execution_success/sha256/src/main.nr index d4240ded8b3..4f999d349f0 100644 --- a/test_programs/execution_success/sha256/src/main.nr +++ b/test_programs/execution_success/sha256/src/main.nr @@ -9,7 +9,6 @@ // // Not yet here: For R1CS, it is more about manipulating arithmetic gates to get performance // This can be done in ACIR! -use dep::std; fn main(x: Field, result: [u8; 32]) { // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field diff --git a/test_programs/execution_success/sha2_byte/src/main.nr b/test_programs/execution_success/sha2_byte/src/main.nr index fa8ddfbdf69..aecd9fba2f3 100644 --- a/test_programs/execution_success/sha2_byte/src/main.nr +++ b/test_programs/execution_success/sha2_byte/src/main.nr @@ -1,5 +1,4 @@ // Test Noir implementations of SHA256 and SHA512 on a one-byte message. -use dep::std; fn main(x: Field, result256: [u8; 32], result512: [u8; 64]) { let digest256 = std::sha256::digest([x as u8]); diff --git a/test_programs/execution_success/signed_comparison/src/main.nr b/test_programs/execution_success/signed_comparison/src/main.nr index d020be380fb..0fe72112f5a 100644 --- a/test_programs/execution_success/signed_comparison/src/main.nr +++ b/test_programs/execution_success/signed_comparison/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(mut x: i8, mut y: i8, z: i8) { let mut s1: i8 = 5; let mut s2: i8 = 8; diff --git a/test_programs/execution_success/signed_division/src/main.nr b/test_programs/execution_success/signed_division/src/main.nr index 6289a2f9ed9..207ef59986b 100644 --- a/test_programs/execution_success/signed_division/src/main.nr +++ b/test_programs/execution_success/signed_division/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Testing signed integer division: // 7/3 = 2 // -7/3 = -2 diff --git a/test_programs/execution_success/simple_print/src/main.nr b/test_programs/execution_success/simple_print/src/main.nr index 6038b995af0..3a68f8cc4c3 100644 --- a/test_programs/execution_success/simple_print/src/main.nr +++ b/test_programs/execution_success/simple_print/src/main.nr @@ -1,6 +1,5 @@ // Simple program for testing the logging // of single witnesses and witness arrays. -use dep::std; fn main(x: Field, y: pub Field) { std::println(x); diff --git a/test_programs/execution_success/simple_shield/src/main.nr b/test_programs/execution_success/simple_shield/src/main.nr index 548ba17d462..d84288b9fd6 100644 --- a/test_programs/execution_success/simple_shield/src/main.nr +++ b/test_programs/execution_success/simple_shield/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main( // Public key of note // all notes have the same denomination diff --git a/test_programs/execution_success/slice_coercion/src/main.nr b/test_programs/execution_success/slice_coercion/src/main.nr index a7ba0443bd1..4a83b739523 100644 --- a/test_programs/execution_success/slice_coercion/src/main.nr +++ b/test_programs/execution_success/slice_coercion/src/main.nr @@ -23,5 +23,5 @@ fn main(expected: pub Field, first: Field) { fn regression_4967() { let var1: [(i32, u8)] = [(1, 2)]; assert(var1.len() == 1); - dep::std::println(var1); + std::println(var1); } diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr index b20e3478898..8be79cdc3c4 100644 --- a/test_programs/execution_success/slices/src/main.nr +++ b/test_programs/execution_success/slices/src/main.nr @@ -1,5 +1,4 @@ -use dep::std::slice; -use dep::std; +use std::slice; fn main(x: Field, y: pub Field) { let mut slice = &[0; 2]; diff --git a/test_programs/execution_success/strings/src/main.nr b/test_programs/execution_success/strings/src/main.nr index cff229d368a..d28a9f483ac 100644 --- a/test_programs/execution_success/strings/src/main.nr +++ b/test_programs/execution_success/strings/src/main.nr @@ -1,4 +1,3 @@ -use dep::std; // Test global string literals global HELLO_WORLD = "hello world"; diff --git a/test_programs/execution_success/to_bits/src/main.nr b/test_programs/execution_success/to_bits/src/main.nr index 18f65f0bd66..84ace83903a 100644 --- a/test_programs/execution_success/to_bits/src/main.nr +++ b/test_programs/execution_success/to_bits/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { let field = 1000; let be_bits = field.to_be_bits(16); diff --git a/test_programs/execution_success/to_bytes_integration/src/main.nr b/test_programs/execution_success/to_bytes_integration/src/main.nr index 3c43caf1806..21c4ad90bfe 100644 --- a/test_programs/execution_success/to_bytes_integration/src/main.nr +++ b/test_programs/execution_success/to_bytes_integration/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(x: Field, a: Field) { let y: Field = 2040124; let be_byte_array = y.to_be_bytes(31); diff --git a/test_programs/execution_success/trait_method_mut_self/src/main.nr b/test_programs/execution_success/trait_method_mut_self/src/main.nr index fa47fd5d881..9cc8375b140 100644 --- a/test_programs/execution_success/trait_method_mut_self/src/main.nr +++ b/test_programs/execution_success/trait_method_mut_self/src/main.nr @@ -1,5 +1,5 @@ -use dep::std::hash::Hasher; -use dep::std::hash::poseidon2::Poseidon2Hasher; +use std::hash::Hasher; +use std::hash::poseidon2::Poseidon2Hasher; fn main(x: Field, y: pub Field) { let mut a_mut_ref = AType { x }; diff --git a/test_programs/execution_success/traits_in_crates_1/crate1/src/lib.nr b/test_programs/execution_success/traits_in_crates_1/crate1/src/lib.nr index 62dd5a2c111..e36a263093a 100644 --- a/test_programs/execution_success/traits_in_crates_1/crate1/src/lib.nr +++ b/test_programs/execution_success/traits_in_crates_1/crate1/src/lib.nr @@ -2,7 +2,7 @@ trait MyTrait { fn Add10(&mut self); } -impl MyTrait for dep::crate2::MyStruct { +impl MyTrait for crate2::MyStruct { fn Add10(&mut self) { self.Q += 10; } diff --git a/test_programs/execution_success/traits_in_crates_1/src/main.nr b/test_programs/execution_success/traits_in_crates_1/src/main.nr index 7ba2f63c5c0..2afec29ee1f 100644 --- a/test_programs/execution_success/traits_in_crates_1/src/main.nr +++ b/test_programs/execution_success/traits_in_crates_1/src/main.nr @@ -1,5 +1,5 @@ fn main(x: Field, y: pub Field) { - let mut V = dep::crate2::MyStruct { Q: x }; + let mut V = crate2::MyStruct { Q: x }; V.Add10(); assert(V.Q == y); } diff --git a/test_programs/execution_success/traits_in_crates_2/crate2/src/lib.nr b/test_programs/execution_success/traits_in_crates_2/crate2/src/lib.nr index 38870489131..fe6a94a4a95 100644 --- a/test_programs/execution_success/traits_in_crates_2/crate2/src/lib.nr +++ b/test_programs/execution_success/traits_in_crates_2/crate2/src/lib.nr @@ -2,7 +2,7 @@ struct MyStruct { Q: Field, } -impl dep::crate1::MyTrait for MyStruct { +impl crate1::MyTrait for MyStruct { fn Add10(&mut self) { self.Q += 10; } diff --git a/test_programs/execution_success/traits_in_crates_2/src/main.nr b/test_programs/execution_success/traits_in_crates_2/src/main.nr index 7ba2f63c5c0..2afec29ee1f 100644 --- a/test_programs/execution_success/traits_in_crates_2/src/main.nr +++ b/test_programs/execution_success/traits_in_crates_2/src/main.nr @@ -1,5 +1,5 @@ fn main(x: Field, y: pub Field) { - let mut V = dep::crate2::MyStruct { Q: x }; + let mut V = crate2::MyStruct { Q: x }; V.Add10(); assert(V.Q == y); } diff --git a/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr b/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr index b880d3ae047..36c7d2926c1 100644 --- a/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr +++ b/test_programs/execution_success/turbofish_call_func_diff_types/src/main.nr @@ -1,6 +1,6 @@ -use dep::std::hash::Hasher; -use dep::std::hash::poseidon2::Poseidon2Hasher; -use dep::std::hash::poseidon::PoseidonHasher; +use std::hash::Hasher; +use std::hash::poseidon2::Poseidon2Hasher; +use std::hash::poseidon::PoseidonHasher; fn main(x: Field, y: pub Field) { let mut hasher = PoseidonHasher::default(); diff --git a/test_programs/execution_success/u128/src/main.nr b/test_programs/execution_success/u128/src/main.nr index a403571ea74..d0835ccf30f 100644 --- a/test_programs/execution_success/u128/src/main.nr +++ b/test_programs/execution_success/u128/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(mut x: u32, y: u32, z: u32, big_int: U128, hexa: str<7>) { let a = U128::from_u64s_le(x as u64, x as u64); let b = U128::from_u64s_le(y as u64, x as u64); diff --git a/test_programs/execution_success/unit_value/src/main.nr b/test_programs/execution_success/unit_value/src/main.nr index f3844e03cf2..a488f267b4c 100644 --- a/test_programs/execution_success/unit_value/src/main.nr +++ b/test_programs/execution_success/unit_value/src/main.nr @@ -1,5 +1,5 @@ fn get_transaction() { - dep::std::unsafe::zeroed() + std::unsafe::zeroed() } fn main() { diff --git a/test_programs/execution_success/wrapping_operations/src/main.nr b/test_programs/execution_success/wrapping_operations/src/main.nr index 85fd65b193c..d8345884c82 100644 --- a/test_programs/execution_success/wrapping_operations/src/main.nr +++ b/test_programs/execution_success/wrapping_operations/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(x: u8, y: u8) { assert(std::wrapping_sub(x, 1) == y); assert(std::wrapping_add(y, 1) == x); diff --git a/test_programs/format.sh b/test_programs/format.sh index 3c679b8689e..fa63d228752 100755 --- a/test_programs/format.sh +++ b/test_programs/format.sh @@ -2,7 +2,7 @@ set -e # These tests are incompatible with gas reporting -excluded_dirs=("workspace" "workspace_default_member" "workspace_reexport_bug") +excluded_dirs=("workspace" "overlapping_dep_and_mod" "overlapping_dep_and_mod_fix" "workspace_default_member" "workspace_reexport_bug") # These tests cause failures in CI with a stack overflow for some reason. ci_excluded_dirs=("eddsa") diff --git a/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr b/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr index 253e999ce07..08a9234a752 100644 --- a/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr +++ b/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr @@ -11,5 +11,5 @@ fn test_with_extra_space() { #[test(should_fail_with = "Not equal")] fn test_runtime_mismatch() { // We use a pedersen commitment here so that the assertion failure is only known at runtime. - assert_eq(dep::std::hash::pedersen_commitment([27]).x, 0, "Not equal "); + assert_eq(std::hash::pedersen_commitment([27]).x, 0, "Not equal "); } diff --git a/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr b/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr index 5d73ef96d49..35a0c44218f 100644 --- a/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr +++ b/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::field::bn254::{TWO_POW_128, assert_gt}; +use std::field::bn254::{TWO_POW_128, assert_gt}; #[test(should_fail_with = "attempt to add with overflow")] unconstrained fn test_overflow_add() { diff --git a/test_programs/noir_test_success/embedded_curve_ops/src/main.nr b/test_programs/noir_test_success/embedded_curve_ops/src/main.nr index 9e3c5d87874..225e86397fd 100644 --- a/test_programs/noir_test_success/embedded_curve_ops/src/main.nr +++ b/test_programs/noir_test_success/embedded_curve_ops/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul}; +use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul}; #[test] diff --git a/test_programs/noir_test_success/field_comparisons/src/main.nr b/test_programs/noir_test_success/field_comparisons/src/main.nr index 105d82ca755..8613e6d6c4f 100644 --- a/test_programs/noir_test_success/field_comparisons/src/main.nr +++ b/test_programs/noir_test_success/field_comparisons/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::field::bn254::{TWO_POW_128, assert_gt}; +use std::field::bn254::{TWO_POW_128, assert_gt}; #[test(should_fail)] fn test_assert_gt_should_fail_eq() { diff --git a/test_programs/noir_test_success/mock_oracle/src/main.nr b/test_programs/noir_test_success/mock_oracle/src/main.nr index d840ffaef66..4d3dd8d030b 100644 --- a/test_programs/noir_test_success/mock_oracle/src/main.nr +++ b/test_programs/noir_test_success/mock_oracle/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::test::OracleMock; +use std::test::OracleMock; struct Point { x: Field, diff --git a/test_programs/noir_test_success/regression_4561/src/main.nr b/test_programs/noir_test_success/regression_4561/src/main.nr index 138f7e805b3..70c447b49af 100644 --- a/test_programs/noir_test_success/regression_4561/src/main.nr +++ b/test_programs/noir_test_success/regression_4561/src/main.nr @@ -1,5 +1,5 @@ // Regression test for issue #4561 -use dep::std::test::OracleMock; +use std::test::OracleMock; type TReturnElem = [Field; 3]; type TReturn = [TReturnElem; 2]; @@ -41,4 +41,4 @@ unconstrained fn two_nested_return_unconstrained() -> (Field, TReturn, Field, TR fn two_nested_return() { OracleMock::mock("two_nested_return").returns((0, [1, 2, 3, 4, 5, 6], 7, [1, 2, 3, 4, 5, 6])); assert_eq(two_nested_return_unconstrained(), (0, [[1, 2, 3], [4, 5, 6]], 7, [[1, 2, 3], [4, 5, 6]])); -} \ No newline at end of file +} diff --git a/test_programs/noir_test_success/should_fail_with_matches/src/main.nr b/test_programs/noir_test_success/should_fail_with_matches/src/main.nr index 1f5c29e58a2..b713976643b 100644 --- a/test_programs/noir_test_success/should_fail_with_matches/src/main.nr +++ b/test_programs/noir_test_success/should_fail_with_matches/src/main.nr @@ -10,21 +10,21 @@ fn test_should_fail_without_match() { #[test(should_fail_with = "Not equal")] fn test_should_fail_with_runtime_match() { - assert_eq(dep::std::hash::pedersen_commitment([27]).x, 0, "Not equal"); + assert_eq(std::hash::pedersen_commitment([27]).x, 0, "Not equal"); } #[test(should_fail)] fn test_should_fail_without_runtime_match() { - assert_eq(dep::std::hash::pedersen_commitment([27]).x, 0); + assert_eq(std::hash::pedersen_commitment([27]).x, 0); } struct InvalidPointError { - point: dep::std::embedded_curve_ops::EmbeddedCurvePoint, + point: std::embedded_curve_ops::EmbeddedCurvePoint, } #[test(should_fail_with = "InvalidPointError { point: EmbeddedCurvePoint { x: 0x1cea3a116d01eb94d568ef04c3dfbc39f96f015ed801ab8958e360d406503ce0, y: 0x2721b237df87234acc36a238b8f231a3d31d18fe3845fff4cc59f0bd873818f8, is_infinite: false } }")] fn test_should_fail_with_struct() { - let hash = dep::std::hash::pedersen_commitment([27]); + let hash = std::hash::pedersen_commitment([27]); assert_eq(hash.x, 0, InvalidPointError { point: hash }); } @@ -37,7 +37,7 @@ fn test_should_fail_with_basic_type_fmt_string() { #[test(should_fail_with = "Invalid hash: EmbeddedCurvePoint { x: 0x1cea3a116d01eb94d568ef04c3dfbc39f96f015ed801ab8958e360d406503ce0, y: 0x2721b237df87234acc36a238b8f231a3d31d18fe3845fff4cc59f0bd873818f8, is_infinite: false }")] fn test_should_fail_with_struct_fmt_string() { - let hash = dep::std::hash::pedersen_commitment([27]); + let hash = std::hash::pedersen_commitment([27]); assert_eq(hash.x, 0, f"Invalid hash: {hash}"); } @@ -55,17 +55,17 @@ unconstrained fn unconstrained_test_should_fail_without_match() { #[test(should_fail_with = "Not equal")] unconstrained fn unconstrained_test_should_fail_with_runtime_match() { - assert_eq(dep::std::hash::pedersen_commitment([27]).x, 0, "Not equal"); + assert_eq(std::hash::pedersen_commitment([27]).x, 0, "Not equal"); } #[test(should_fail)] unconstrained fn unconstrained_test_should_fail_without_runtime_match() { - assert_eq(dep::std::hash::pedersen_commitment([27]).x, 0); + assert_eq(std::hash::pedersen_commitment([27]).x, 0); } #[test(should_fail_with = "InvalidPointError { point: EmbeddedCurvePoint { x: 0x1cea3a116d01eb94d568ef04c3dfbc39f96f015ed801ab8958e360d406503ce0, y: 0x2721b237df87234acc36a238b8f231a3d31d18fe3845fff4cc59f0bd873818f8, is_infinite: false } }")] unconstrained fn unconstrained_test_should_fail_with_struct() { - let hash = dep::std::hash::pedersen_commitment([27]); + let hash = std::hash::pedersen_commitment([27]); assert_eq(hash.x, 0, InvalidPointError { point: hash }); } @@ -78,6 +78,6 @@ unconstrained fn unconstrained_test_should_fail_with_basic_type_fmt_string() { #[test(should_fail_with = "Invalid hash: EmbeddedCurvePoint { x: 0x1cea3a116d01eb94d568ef04c3dfbc39f96f015ed801ab8958e360d406503ce0, y: 0x2721b237df87234acc36a238b8f231a3d31d18fe3845fff4cc59f0bd873818f8, is_infinite: false }")] unconstrained fn unconstrained_test_should_fail_with_struct_fmt_string() { - let hash = dep::std::hash::pedersen_commitment([27]); + let hash = std::hash::pedersen_commitment([27]); assert_eq(hash.x, 0, f"Invalid hash: {hash}"); } diff --git a/test_programs/rebuild.sh b/test_programs/rebuild.sh index 2d4147cb08c..094c3902583 100755 --- a/test_programs/rebuild.sh +++ b/test_programs/rebuild.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -e -NO_PARALLEL=${1:-} - process_dir() { local dir=$1 local current_dir=$2 @@ -62,28 +60,7 @@ for dir in $current_dir/benchmarks/*; do dirs_to_process+=("$dir") done -# Process each directory in parallel -pids=() -if [ -z $NO_PARALLEL ]; then -for dir in "${dirs_to_process[@]}"; do - process_dir "$dir" "$current_dir" & - pids+=($!) -done -else -for dir in "${dirs_to_process[@]}"; do - process_dir "$dir" "$current_dir" - pids+=($!) -done -fi -# Check the exit status of each background job. -for pid in "${pids[@]}"; do - wait $pid || exit_status=$? -done +parallel -j0 process_dir {} "$current_dir" ::: ${dirs_to_process[@]} -# Exit with a failure status if any job failed. -if [ ! -z "$exit_status" ]; then - echo "Rebuild failed!" - exit $exit_status -fi echo "Rebuild Succeeded!" diff --git a/test_programs/test_libraries/diamond_deps_1/src/lib.nr b/test_programs/test_libraries/diamond_deps_1/src/lib.nr index 60c001ec64e..d76ce5a05e9 100644 --- a/test_programs/test_libraries/diamond_deps_1/src/lib.nr +++ b/test_programs/test_libraries/diamond_deps_1/src/lib.nr @@ -1,4 +1,4 @@ -use dep::dep2::call_dep2; +use dep2::call_dep2; pub fn call_dep1_then_dep2(x: Field, y: Field) -> Field { call_dep2(x, y) diff --git a/test_programs/test_libraries/exporting_lib/src/lib.nr b/test_programs/test_libraries/exporting_lib/src/lib.nr index bfb1819132a..fdd9f139d41 100644 --- a/test_programs/test_libraries/exporting_lib/src/lib.nr +++ b/test_programs/test_libraries/exporting_lib/src/lib.nr @@ -4,6 +4,6 @@ struct MyStruct { type FooStruct = MyStruct; -fn is_struct_zero(val: MyStruct) -> bool { +pub fn is_struct_zero(val: MyStruct) -> bool { val.inner == 0 } diff --git a/test_programs/test_libraries/reexporting_lib/src/lib.nr b/test_programs/test_libraries/reexporting_lib/src/lib.nr index f12dfe01ecd..1bced548304 100644 --- a/test_programs/test_libraries/reexporting_lib/src/lib.nr +++ b/test_programs/test_libraries/reexporting_lib/src/lib.nr @@ -1,3 +1,3 @@ -use dep::exporting_lib::{MyStruct, FooStruct}; +use exporting_lib::{MyStruct, FooStruct}; -use dep::exporting_lib as lib; +use exporting_lib as lib; diff --git a/tooling/debugger/src/source_code_printer.rs b/tooling/debugger/src/source_code_printer.rs index e298eb8aadd..ad511563477 100644 --- a/tooling/debugger/src/source_code_printer.rs +++ b/tooling/debugger/src/source_code_printer.rs @@ -143,7 +143,7 @@ fn render_line( // // Consider for example the file (line numbers added to facilitate this doc): // ``` -// 1 use dep::std::hash::poseidon; +// 1 use std::hash::poseidon; // 2 // 3 fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { // 4 let hash1 = poseidon::bn254::hash_2(x1); @@ -157,7 +157,7 @@ fn render_line( // // If the location to render is `poseidon::bn254::hash_2(x1)`, we'll render the file as: // ``` -// 1 use dep::std::hash::poseidon; +// 1 use std::hash::poseidon; // 2 // 3 fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { // 4 let hash1 = poseidon::bn254::hash_2(x1); diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr index 97a6ebd6b77..e3a5877725a 100644 --- a/tooling/nargo_fmt/tests/expected/contract.nr +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -3,11 +3,11 @@ // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. // Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. contract Benchmarking { - use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; + use aztec::protocol_types::abis::function_selector::FunctionSelector; - use dep::value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; + use value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; - use dep::aztec::{ + use aztec::{ context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, diff --git a/tooling/nargo_fmt/tests/expected/import_braces.nr b/tooling/nargo_fmt/tests/expected/import_braces.nr index 49c9d09001e..9c74c477f5f 100644 --- a/tooling/nargo_fmt/tests/expected/import_braces.nr +++ b/tooling/nargo_fmt/tests/expected/import_braces.nr @@ -1 +1 @@ -use dep::std::hash::sha256; +use std::hash::sha256; diff --git a/tooling/nargo_fmt/tests/expected/let.nr b/tooling/nargo_fmt/tests/expected/let.nr index 7ff69e74306..0edc0eaf922 100644 --- a/tooling/nargo_fmt/tests/expected/let.nr +++ b/tooling/nargo_fmt/tests/expected/let.nr @@ -51,10 +51,10 @@ fn let_() { let expr = MyExpr { /*A boolean literal (true, false).*/ kind: ExprKind::Bool(true) }; - let mut V = dep::crate2::MyStruct { Q: x }; - let mut V = dep::crate2::MyStruct {}; - let mut V = dep::crate2::MyStruct {/*test*/}; - let mut V = dep::crate2::MyStruct { + let mut V = crate2::MyStruct { Q: x }; + let mut V = crate2::MyStruct {}; + let mut V = crate2::MyStruct {/*test*/}; + let mut V = crate2::MyStruct { // sad }; } diff --git a/tooling/nargo_fmt/tests/expected/print.nr b/tooling/nargo_fmt/tests/expected/print.nr index 3bce0941da2..d8404f674b0 100644 --- a/tooling/nargo_fmt/tests/expected/print.nr +++ b/tooling/nargo_fmt/tests/expected/print.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { std::print("Hello world"); std::println("Hello world"); diff --git a/tooling/nargo_fmt/tests/expected/print2.nr b/tooling/nargo_fmt/tests/expected/print2.nr index 3bce0941da2..d8404f674b0 100644 --- a/tooling/nargo_fmt/tests/expected/print2.nr +++ b/tooling/nargo_fmt/tests/expected/print2.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { std::print("Hello world"); std::println("Hello world"); diff --git a/tooling/nargo_fmt/tests/expected/singleton_import.nr b/tooling/nargo_fmt/tests/expected/singleton_import.nr new file mode 100644 index 00000000000..bb1bad500d9 --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/singleton_import.nr @@ -0,0 +1,2 @@ +use dep::std; +use some_crate; diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr index 97a6ebd6b77..e3a5877725a 100644 --- a/tooling/nargo_fmt/tests/input/contract.nr +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -3,11 +3,11 @@ // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. // Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. contract Benchmarking { - use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; + use aztec::protocol_types::abis::function_selector::FunctionSelector; - use dep::value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; + use value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; - use dep::aztec::{ + use aztec::{ context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, diff --git a/tooling/nargo_fmt/tests/input/import_braces.nr b/tooling/nargo_fmt/tests/input/import_braces.nr index 88c7e9562a8..0647bbaa580 100644 --- a/tooling/nargo_fmt/tests/input/import_braces.nr +++ b/tooling/nargo_fmt/tests/input/import_braces.nr @@ -1 +1 @@ -use dep::std::hash::{sha256}; \ No newline at end of file +use std::hash::{sha256}; diff --git a/tooling/nargo_fmt/tests/input/let.nr b/tooling/nargo_fmt/tests/input/let.nr index 37cdc6655c7..16ce0a9d7f1 100644 --- a/tooling/nargo_fmt/tests/input/let.nr +++ b/tooling/nargo_fmt/tests/input/let.nr @@ -26,10 +26,10 @@ kind: ExprKind::Bool(true), let expr = MyExpr {/*A boolean literal (true, false).*/kind: ExprKind::Bool(true),}; - let mut V = dep::crate2::MyStruct { Q: x }; - let mut V = dep::crate2::MyStruct {}; - let mut V = dep::crate2::MyStruct {/*test*/}; - let mut V = dep::crate2::MyStruct { + let mut V = crate2::MyStruct { Q: x }; + let mut V = crate2::MyStruct {}; + let mut V = crate2::MyStruct {/*test*/}; + let mut V = crate2::MyStruct { // sad }; } diff --git a/tooling/nargo_fmt/tests/input/print.nr b/tooling/nargo_fmt/tests/input/print.nr index 3bce0941da2..d8404f674b0 100644 --- a/tooling/nargo_fmt/tests/input/print.nr +++ b/tooling/nargo_fmt/tests/input/print.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { std::print("Hello world"); std::println("Hello world"); diff --git a/tooling/nargo_fmt/tests/input/print2.nr b/tooling/nargo_fmt/tests/input/print2.nr index 3bce0941da2..d8404f674b0 100644 --- a/tooling/nargo_fmt/tests/input/print2.nr +++ b/tooling/nargo_fmt/tests/input/print2.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main() { std::print("Hello world"); std::println("Hello world"); diff --git a/tooling/nargo_fmt/tests/input/singleton_import.nr b/tooling/nargo_fmt/tests/input/singleton_import.nr new file mode 100644 index 00000000000..bb1bad500d9 --- /dev/null +++ b/tooling/nargo_fmt/tests/input/singleton_import.nr @@ -0,0 +1,2 @@ +use dep::std; +use some_crate; diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr index a9aaae5f2f7..5a20a92048f 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr @@ -1,5 +1,3 @@ -use dep::std; - fn main(x: u64, y: pub u64) -> pub u64 { // We include a println statement to show that noirJS will ignore this and continue execution std::println("foo"); From 540bef3597bd3e5775c83ec2ee3c0d4463084b4c Mon Sep 17 00:00:00 2001 From: noirwhal <163862677+noirwhal@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:30:02 +0100 Subject: [PATCH 37/57] chore: Release Noir(0.31.0) (#5166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* ---

0.31.0 ## [0.31.0](https://github.com/noir-lang/noir/compare/v0.30.0...v0.31.0) (2024-06-17) ### ⚠ BREAKING CHANGES * remove `dep::` prefix ([#4946](https://github.com/noir-lang/noir/issues/4946)) * remove `distinct` keyword ([#5219](https://github.com/noir-lang/noir/issues/5219)) * remove `param_witnesses` and `return_witnesses` from ABI ([#5154](https://github.com/noir-lang/noir/issues/5154)) * add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) * restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) * separate proving from `noir_js` ([#5072](https://github.com/noir-lang/noir/issues/5072)) * switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) * specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) * **stdlib:** eddsa function using turbofish ([#5050](https://github.com/noir-lang/noir/issues/5050)) ### Features * `pxe.addNullifiedNote(...)` (https://github.com/AztecProtocol/aztec-packages/pull/6948) ([7de19f5](https://github.com/noir-lang/noir/commit/7de19f5856591203271836f07154abae13f5102b)) * Activate return_data in ACIR opcodes ([#5080](https://github.com/noir-lang/noir/issues/5080)) ([c9fda3c](https://github.com/noir-lang/noir/commit/c9fda3c7fd4575bfe7d457e8d4230e071f0129a0)) * Add `as_witness` builtin function in order to constrain a witness to be equal to a variable ([#4641](https://github.com/noir-lang/noir/issues/4641)) ([faf5bd8](https://github.com/noir-lang/noir/commit/faf5bd8ed80fb89b4bb6a2536b9bfa9649579da7)) * Add `set` and `set_unchecked` methods to `Vec` and `BoundedVec` ([#5241](https://github.com/noir-lang/noir/issues/5241)) ([1849389](https://github.com/noir-lang/noir/commit/1849389362e22e8236177f84b735dadf840cd637)) * Add BoundedVec::map ([#5250](https://github.com/noir-lang/noir/issues/5250)) ([da1549c](https://github.com/noir-lang/noir/commit/da1549cfb296261b273a3a64908382e7b71512ad)) * Add intrinsic to get if running inside an unconstrained context ([#5098](https://github.com/noir-lang/noir/issues/5098)) ([281ebf2](https://github.com/noir-lang/noir/commit/281ebf26e4cd16daf361938de505697f8d5fbd5e)) * Add native rust implementation of schnorr signature verification ([#5053](https://github.com/noir-lang/noir/issues/5053)) ([fab1c35](https://github.com/noir-lang/noir/commit/fab1c3567d731ea7902635a7a020a8d14f94fd27)) * Add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) ([14adafc](https://github.com/noir-lang/noir/commit/14adafc965fa9c833e096ec037e086aae67703ad)) * Consider block parameters in variable liveness ([#5097](https://github.com/noir-lang/noir/issues/5097)) ([e4eb5f5](https://github.com/noir-lang/noir/commit/e4eb5f539f377fd3c2e1a874707ffce62a5bc10a)) * **experimental:** Implement macro calls & splicing into `Expr` values ([#5203](https://github.com/noir-lang/noir/issues/5203)) ([d9b4712](https://github.com/noir-lang/noir/commit/d9b4712bf1a62548dd7ed17b181882ae537d70dd)) * Implement println in the comptime interpreter ([#5197](https://github.com/noir-lang/noir/issues/5197)) ([7f08343](https://github.com/noir-lang/noir/commit/7f08343dfcafddfcec1b238746a69273ae4f4e2b)) * Implement turbofish operator ([#3542](https://github.com/noir-lang/noir/issues/3542)) ([226724e](https://github.com/noir-lang/noir/commit/226724e3b54c2e0d9ba005661c76b40a87d9295a)) * Make ACVM generic across fields ([#5114](https://github.com/noir-lang/noir/issues/5114)) ([70f374c](https://github.com/noir-lang/noir/commit/70f374c06642962d8f2b95b80f8c938fcf7761d7)) * Move abi demonomorphizer to noir_codegen and use noir_codegen in protocol types (https://github.com/AztecProtocol/aztec-packages/pull/6302) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Move to_radix to a blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6294) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * **nargo:** Hidden option to show contract artifact paths written by `nargo compile` (https://github.com/AztecProtocol/aztec-packages/pull/6131) ([ff67e14](https://github.com/noir-lang/noir/commit/ff67e145d086bf6fdf58fb5e57927033e52e03d3)) * Place return value witnesses directly after function arguments ([#5142](https://github.com/noir-lang/noir/issues/5142)) ([1252b5f](https://github.com/noir-lang/noir/commit/1252b5fcc7ed56bb55e95745b83be6e556805397)) * Private Kernel Recursion (https://github.com/AztecProtocol/aztec-packages/pull/6278) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Proper padding in ts AES and constrained AES in body and header computations (https://github.com/AztecProtocol/aztec-packages/pull/6269) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Remove `dep::` prefix ([#4946](https://github.com/noir-lang/noir/issues/4946)) ([d6d0ae2](https://github.com/noir-lang/noir/commit/d6d0ae26d2fef083dc240539b834d934c84b0326)) * Remove conditional compilation of `bn254_blackbox_solver` ([#5058](https://github.com/noir-lang/noir/issues/5058)) ([9420d7c](https://github.com/noir-lang/noir/commit/9420d7c2ba6bbbf5ecb9a066837c505310955b6c)) * Remove external blackbox solver from acir simulator (https://github.com/AztecProtocol/aztec-packages/pull/6586) ([a40a9a5](https://github.com/noir-lang/noir/commit/a40a9a55571deed386688fb84260bdf2794d4d38)) * Replace stdlib poseidon implementation with optimized version ([#5122](https://github.com/noir-lang/noir/issues/5122)) ([11e98f3](https://github.com/noir-lang/noir/commit/11e98f348d1d43a9b28d83ec3308027b7afc0da6)) * Restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) ([bdb2bc6](https://github.com/noir-lang/noir/commit/bdb2bc608ea8fd52d46545a38b68dd2558b28110)) * Separate proving from `noir_js` ([#5072](https://github.com/noir-lang/noir/issues/5072)) ([c93c738](https://github.com/noir-lang/noir/commit/c93c7380c705fcec5c77bfc436c2f5ea085edd77)) * Separate runtimes of SSA functions before inlining ([#5121](https://github.com/noir-lang/noir/issues/5121)) ([69eca9b](https://github.com/noir-lang/noir/commit/69eca9b8671fa54192bef814dd584fdb5387a5f7)) * Specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Standardize pedersen functions to return `EmbeddedCurvePoint` ([#5190](https://github.com/noir-lang/noir/issues/5190)) ([3b85b36](https://github.com/noir-lang/noir/commit/3b85b3637f81f3894a7faa07fd299f9d64747214)) * **stdlib:** Eddsa function using turbofish ([#5050](https://github.com/noir-lang/noir/issues/5050)) ([7936262](https://github.com/noir-lang/noir/commit/79362629ed8cf42b6601e9a551ed8f9fe03e0112)) * Support casting in globals ([#5164](https://github.com/noir-lang/noir/issues/5164)) ([6d3e732](https://github.com/noir-lang/noir/commit/6d3e732e06033b53506656acdd3d7759bd27f106)) * Switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6280) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6332) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6573) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6986) ([7de19f5](https://github.com/noir-lang/noir/commit/7de19f5856591203271836f07154abae13f5102b)) * ToRadix BB + avm transpiler support (https://github.com/AztecProtocol/aztec-packages/pull/6330) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) ### Bug Fixes * Add support for nested arrays returned by oracles ([#5132](https://github.com/noir-lang/noir/issues/5132)) ([f846879](https://github.com/noir-lang/noir/commit/f846879dd038328bd0a1d39a72b448ef52a1002b)) * Apply self type from generic trait constraint before instantiating identifiers ([#5087](https://github.com/noir-lang/noir/issues/5087)) ([2b4755c](https://github.com/noir-lang/noir/commit/2b4755c2b57460d5eb839ee835f8c9acd5773a7c)) * Auto dereference trait methods in the elaborator ([#5124](https://github.com/noir-lang/noir/issues/5124)) ([56c1a85](https://github.com/noir-lang/noir/commit/56c1a85056ed338644595f1aa58cc94563786b9e)) * Check for public args in aztec functions (https://github.com/AztecProtocol/aztec-packages/pull/6355) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Disable `if` optimization ([#5240](https://github.com/noir-lang/noir/issues/5240)) ([a2816db](https://github.com/noir-lang/noir/commit/a2816dbf7f9d31967fc95205a43fdfdf181029b0)) * **elaborator:** Fix duplicate methods error ([#5225](https://github.com/noir-lang/noir/issues/5225)) ([87a1d8e](https://github.com/noir-lang/noir/commit/87a1d8ebaadb5f0f1ed637b96816f971f946af87)) * **elaborator:** Fix regression introduced by lazy-global changes ([#5223](https://github.com/noir-lang/noir/issues/5223)) ([fde432a](https://github.com/noir-lang/noir/commit/fde432aacc436b6c57f0d937d7c86836bac0b465)) * **elaborator:** Invert unconstrained check ([#5176](https://github.com/noir-lang/noir/issues/5176)) ([967c0fa](https://github.com/noir-lang/noir/commit/967c0fa76da9384afe918a8b23eef60f12f29292)) * **elaborator:** Lazily elaborate globals ([#5191](https://github.com/noir-lang/noir/issues/5191)) ([9c99a97](https://github.com/noir-lang/noir/commit/9c99a97ca8f42bee23cf97ebd724fdc51e647c60)) * Error for allocate instructions in acir-gen ([#5200](https://github.com/noir-lang/noir/issues/5200)) ([58c7532](https://github.com/noir-lang/noir/commit/58c7532da8dd86ee02b20d7e7809f5437f667845)) * **experimental elaborator:** Avoid calling `add_generics` twice on trait methods ([#5108](https://github.com/noir-lang/noir/issues/5108)) ([7d8c0a3](https://github.com/noir-lang/noir/commit/7d8c0a3a1ae143b574b2fa62cae7c0a493005c70)) * **experimental elaborator:** Clear generics after elaborating type aliases ([#5136](https://github.com/noir-lang/noir/issues/5136)) ([b0a7d0b](https://github.com/noir-lang/noir/commit/b0a7d0b12328d3ed9faed87b78792b77786018e0)) * **experimental elaborator:** Fix `impl Trait` when `--use-elaborator` is selected ([#5138](https://github.com/noir-lang/noir/issues/5138)) ([7ea5962](https://github.com/noir-lang/noir/commit/7ea5962e77b7183374a4e14da3a237ccd63f00a0)) * **experimental elaborator:** Fix definition kind of globals and tuple patterns with `--use-elaborator` flag ([#5139](https://github.com/noir-lang/noir/issues/5139)) ([a140dec](https://github.com/noir-lang/noir/commit/a140dec4580459c5856d44337de3ea08aa7fb44a)) * **experimental elaborator:** Fix duplicate `resolve_type` on self type and don't leak a trait impl's generics ([#5102](https://github.com/noir-lang/noir/issues/5102)) ([db561e2](https://github.com/noir-lang/noir/commit/db561e229cfcb35f23205cbb7e41fcf5ece68ee5)) * **experimental elaborator:** Fix frontend tests when `--use-elaborator` flag is specified ([#5145](https://github.com/noir-lang/noir/issues/5145)) ([d6122eb](https://github.com/noir-lang/noir/commit/d6122eb9e88aa2b1bb6c990e452fa9678ae49704)) * **experimental elaborator:** Fix global values used in the elaborator ([#5135](https://github.com/noir-lang/noir/issues/5135)) ([e73cdbb](https://github.com/noir-lang/noir/commit/e73cdbb93b0714331fef754f862d89c08c28a9e5)) * **experimental elaborator:** Fix globals which use function calls ([#5172](https://github.com/noir-lang/noir/issues/5172)) ([ab0b1a8](https://github.com/noir-lang/noir/commit/ab0b1a85cc91f8ed748ee393ece54f5c3b43d7ef)) * **experimental elaborator:** Fix panic in the elaborator ([#5082](https://github.com/noir-lang/noir/issues/5082)) ([ffcb410](https://github.com/noir-lang/noir/commit/ffcb410978a362c73783fbfe5bbdc9691499609e)) * **experimental elaborator:** Only call `add_generics` once ([#5091](https://github.com/noir-lang/noir/issues/5091)) ([f5d2946](https://github.com/noir-lang/noir/commit/f5d294645e82fc85d8dc28ee2a846ba11af85ce5)) * Fix panic in `get_global_let_statement` ([#5177](https://github.com/noir-lang/noir/issues/5177)) ([b769b01](https://github.com/noir-lang/noir/commit/b769b01fd06a6a2c66c72f9aa4e1d346b0fca123)) * **frontend:** Call trait method with mut self from generic definition ([#5041](https://github.com/noir-lang/noir/issues/5041)) ([89846cf](https://github.com/noir-lang/noir/commit/89846cfbc4961c5258d91b5973f027be80885a20)) * **frontend:** Correctly monomorphize turbofish functions ([#5049](https://github.com/noir-lang/noir/issues/5049)) ([fd772e7](https://github.com/noir-lang/noir/commit/fd772e7a764004373f5a41a54eb6847f4decda77)) * **frontend:** Resolve object types from method calls a single time ([#5131](https://github.com/noir-lang/noir/issues/5131)) ([3afe023](https://github.com/noir-lang/noir/commit/3afe023543e301aafaf2b79f0ccd6d7936dd53a9)) * Temporarily revert to_radix blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6304) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Use plain integer addresses for opcodes in DAP disassembly view ([#4941](https://github.com/noir-lang/noir/issues/4941)) ([d43ba1b](https://github.com/noir-lang/noir/commit/d43ba1bddbf6ebd56a7bee0e1db38d155fec95d5)) * Use predicate for curve operations ([#5076](https://github.com/noir-lang/noir/issues/5076)) ([145b909](https://github.com/noir-lang/noir/commit/145b90945486907cb6db75d3f3f93a58d19b2a32)) * Wrapping in signed division ([#5134](https://github.com/noir-lang/noir/issues/5134)) ([29baeb4](https://github.com/noir-lang/noir/commit/29baeb41e15918935c437e0a2759c6b936f125a4)) ### Miscellaneous Chores * Remove `distinct` keyword ([#5219](https://github.com/noir-lang/noir/issues/5219)) ([1d62c59](https://github.com/noir-lang/noir/commit/1d62c59a8f02f7d277c5bf9ed637348a3b2f399c)) * Remove `param_witnesses` and `return_witnesses` from ABI ([#5154](https://github.com/noir-lang/noir/issues/5154)) ([21562ae](https://github.com/noir-lang/noir/commit/21562aeea162d246573967115e7c519715f6d3d8))
0.47.0 ## [0.47.0](https://github.com/noir-lang/noir/compare/v0.46.0...v0.47.0) (2024-06-17) ### ⚠ BREAKING CHANGES * add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) * restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) * switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) * specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) * remove `Opcode::Brillig` from ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5995) * AES blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6016) * Bit shift is restricted to u8 right operand ([#4907](https://github.com/noir-lang/noir/issues/4907)) * contract interfaces and better function calls (https://github.com/AztecProtocol/aztec-packages/pull/5687) * change backend width to 4 (https://github.com/AztecProtocol/aztec-packages/pull/5374) * Use fixed size arrays in black box functions where sizes are known (https://github.com/AztecProtocol/aztec-packages/pull/5620) * trap with revert data (https://github.com/AztecProtocol/aztec-packages/pull/5732) * **acir:** BrilligCall opcode (https://github.com/AztecProtocol/aztec-packages/pull/5709) * remove fixed-length keccak256 (https://github.com/AztecProtocol/aztec-packages/pull/5617) * storage_layout and `#[aztec(storage)]` (https://github.com/AztecProtocol/aztec-packages/pull/5387) * **acir:** Add predicate to call opcode (https://github.com/AztecProtocol/aztec-packages/pull/5616) * contract_abi-exports (https://github.com/AztecProtocol/aztec-packages/pull/5386) * Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) * automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) * move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) * note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) * rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) * init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ### Features * `multi_scalar_mul` blackbox func (https://github.com/AztecProtocol/aztec-packages/pull/6097) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * `variable_base_scalar_mul` blackbox func (https://github.com/AztecProtocol/aztec-packages/pull/6039) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **acir_gen:** Brillig stdlib ([#4848](https://github.com/noir-lang/noir/issues/4848)) ([0c8175c](https://github.com/noir-lang/noir/commit/0c8175cb539efd9427c73ae5af0d48abe688ebab)) * **acir_gen:** Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) * **acir:** Add predicate to call opcode (https://github.com/AztecProtocol/aztec-packages/pull/5616) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * **acir:** BrilligCall opcode (https://github.com/AztecProtocol/aztec-packages/pull/5709) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Activate return_data in ACIR opcodes ([#5080](https://github.com/noir-lang/noir/issues/5080)) ([c9fda3c](https://github.com/noir-lang/noir/commit/c9fda3c7fd4575bfe7d457e8d4230e071f0129a0)) * **acvm_js:** Execute program ([#4694](https://github.com/noir-lang/noir/issues/4694)) ([386f6d0](https://github.com/noir-lang/noir/commit/386f6d0a5822912db878285cb001032a7c0ff622)) * **acvm:** Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) * Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) * Add native rust implementation of schnorr signature verification ([#5053](https://github.com/noir-lang/noir/issues/5053)) ([fab1c35](https://github.com/noir-lang/noir/commit/fab1c3567d731ea7902635a7a020a8d14f94fd27)) * Add native rust implementations of pedersen functions ([#4871](https://github.com/noir-lang/noir/issues/4871)) ([fb039f7](https://github.com/noir-lang/noir/commit/fb039f74df23aea39bc0593a5d538d82b4efadf0)) * Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) * Add return values to aztec fns (https://github.com/AztecProtocol/aztec-packages/pull/5389) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * Add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) ([14adafc](https://github.com/noir-lang/noir/commit/14adafc965fa9c833e096ec037e086aae67703ad)) * Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * AES blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6016) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) * Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **avm:** Integrate AVM with initializers (https://github.com/AztecProtocol/aztec-packages/pull/5469) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) * Bit shift is restricted to u8 right operand ([#4907](https://github.com/noir-lang/noir/issues/4907)) ([c4b0369](https://github.com/noir-lang/noir/commit/c4b03691feca17ef268acab523292f3051f672ea)) * Brillig heterogeneous memory cells (https://github.com/AztecProtocol/aztec-packages/pull/5608) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) * Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Brillig pointer codegen and execution (https://github.com/AztecProtocol/aztec-packages/pull/5737) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ([0bc18c4](https://github.com/noir-lang/noir/commit/0bc18c4f78171590dd58bded959f68f53a44cc8c)) * Change backend width to 4 (https://github.com/AztecProtocol/aztec-packages/pull/5374) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Contract interfaces and better function calls (https://github.com/AztecProtocol/aztec-packages/pull/5687) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Contract_abi-exports (https://github.com/AztecProtocol/aztec-packages/pull/5386) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * Dynamic assertion payloads v2 (https://github.com/AztecProtocol/aztec-packages/pull/5949) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) * Handle `BrilligCall` opcodes in the debugger ([#4897](https://github.com/noir-lang/noir/issues/4897)) ([b380dc4](https://github.com/noir-lang/noir/commit/b380dc44de5c9f8de278ece3d531ebbc2c9238ba)) * Impl of missing functionality in new key store (https://github.com/AztecProtocol/aztec-packages/pull/5750) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Increase default expression width to 4 ([#4995](https://github.com/noir-lang/noir/issues/4995)) ([f01d309](https://github.com/noir-lang/noir/commit/f01d3090759a5ff0f1f83c5616d22890c6bd76be)) * Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Make ACVM generic across fields ([#5114](https://github.com/noir-lang/noir/issues/5114)) ([70f374c](https://github.com/noir-lang/noir/commit/70f374c06642962d8f2b95b80f8c938fcf7761d7)) * Move abi demonomorphizer to noir_codegen and use noir_codegen in protocol types (https://github.com/AztecProtocol/aztec-packages/pull/6302) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Move to_radix to a blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6294) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * **nargo:** Handle call stacks for multiple Acir calls ([#4711](https://github.com/noir-lang/noir/issues/4711)) ([5b23171](https://github.com/noir-lang/noir/commit/5b231714740447d82cde7cdbe65d4a8b46a31df4)) * **nargo:** Hidden option to show contract artifact paths written by `nargo compile` (https://github.com/AztecProtocol/aztec-packages/pull/6131) ([ff67e14](https://github.com/noir-lang/noir/commit/ff67e145d086bf6fdf58fb5e57927033e52e03d3)) * New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Parsing non-string assertion payloads in noir js (https://github.com/AztecProtocol/aztec-packages/pull/6079) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * Private Kernel Recursion (https://github.com/AztecProtocol/aztec-packages/pull/6278) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Proper padding in ts AES and constrained AES in body and header computations (https://github.com/AztecProtocol/aztec-packages/pull/6269) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Remove conditional compilation of `bn254_blackbox_solver` ([#5058](https://github.com/noir-lang/noir/issues/5058)) ([9420d7c](https://github.com/noir-lang/noir/commit/9420d7c2ba6bbbf5ecb9a066837c505310955b6c)) * Remove external blackbox solver from acir simulator (https://github.com/AztecProtocol/aztec-packages/pull/6586) ([a40a9a5](https://github.com/noir-lang/noir/commit/a40a9a55571deed386688fb84260bdf2794d4d38)) * Restore hashing args via slice for performance (https://github.com/AztecProtocol/aztec-packages/pull/5539) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * Restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) ([bdb2bc6](https://github.com/noir-lang/noir/commit/bdb2bc608ea8fd52d46545a38b68dd2558b28110)) * Separate runtimes of SSA functions before inlining ([#5121](https://github.com/noir-lang/noir/issues/5121)) ([69eca9b](https://github.com/noir-lang/noir/commit/69eca9b8671fa54192bef814dd584fdb5387a5f7)) * Set aztec private functions to be recursive (https://github.com/AztecProtocol/aztec-packages/pull/6192) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **simulator:** Fetch return values at circuit execution (https://github.com/AztecProtocol/aztec-packages/pull/5642) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) * Specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Storage_layout and `#[aztec(storage)]` (https://github.com/AztecProtocol/aztec-packages/pull/5387) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5572) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5619) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5697) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5794) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5814) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5935) ([1b867b1](https://github.com/noir-lang/noir/commit/1b867b121fba5db3087ca845b4934e6732b23fd1)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5955) ([1b867b1](https://github.com/noir-lang/noir/commit/1b867b121fba5db3087ca845b4934e6732b23fd1)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5999) ([1b867b1](https://github.com/noir-lang/noir/commit/1b867b121fba5db3087ca845b4934e6732b23fd1)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6280) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6332) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6573) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * ToRadix BB + avm transpiler support (https://github.com/AztecProtocol/aztec-packages/pull/6330) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Trap with revert data (https://github.com/AztecProtocol/aztec-packages/pull/5732) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Use fixed size arrays in black box functions where sizes are known (https://github.com/AztecProtocol/aztec-packages/pull/5620) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Variable length returns (https://github.com/AztecProtocol/aztec-packages/pull/5633) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) ### Bug Fixes * **acvm:** Mark outputs of Opcode::Call solvable ([#4708](https://github.com/noir-lang/noir/issues/4708)) ([8fea405](https://github.com/noir-lang/noir/commit/8fea40576f262bd5bb588923c0660d8967404e56)) * Add support for nested arrays returned by oracles ([#5132](https://github.com/noir-lang/noir/issues/5132)) ([f846879](https://github.com/noir-lang/noir/commit/f846879dd038328bd0a1d39a72b448ef52a1002b)) * Avoid huge unrolling in hash_args (https://github.com/AztecProtocol/aztec-packages/pull/5703) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) * Catch panics from EC point creation (e.g. the point is at infinity) ([#4790](https://github.com/noir-lang/noir/issues/4790)) ([645dba1](https://github.com/noir-lang/noir/commit/645dba192f16ef34018828186ffb297422a8dc73)) * Check for public args in aztec functions (https://github.com/AztecProtocol/aztec-packages/pull/6355) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) * Don't reuse brillig with slice arguments (https://github.com/AztecProtocol/aztec-packages/pull/5800) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) * Issue 4682 and add solver for unconstrained bigintegers ([#4729](https://github.com/noir-lang/noir/issues/4729)) ([e4d33c1](https://github.com/noir-lang/noir/commit/e4d33c126a2795d9aaa6048d4e91b64cb4bbe4f2)) * Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Proper field inversion for bigints ([#4802](https://github.com/noir-lang/noir/issues/4802)) ([b46d0e3](https://github.com/noir-lang/noir/commit/b46d0e39f4252f8bbaa987f88d112e4c233b3d61)) * Temporarily revert to_radix blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6304) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) ### Miscellaneous Chores * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove `Opcode::Brillig` from ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5995) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) * Remove fixed-length keccak256 (https://github.com/AztecProtocol/aztec-packages/pull/5617) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) * Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kevaundray --- .release-please-manifest.json | 4 +- CHANGELOG.md | 92 +++ Cargo.lock | 52 +- Cargo.toml | 16 +- acvm-repo/CHANGELOG.md | 148 +++++ acvm-repo/acir/Cargo.toml | 2 +- acvm-repo/acir_field/Cargo.toml | 2 +- acvm-repo/acvm/Cargo.toml | 2 +- acvm-repo/acvm_js/Cargo.toml | 2 +- acvm-repo/acvm_js/package.json | 2 +- acvm-repo/blackbox_solver/Cargo.toml | 2 +- acvm-repo/bn254_blackbox_solver/Cargo.toml | 2 +- acvm-repo/brillig/Cargo.toml | 2 +- acvm-repo/brillig_vm/Cargo.toml | 2 +- compiler/wasm/package.json | 2 +- .../explainers/explainer-oracle.md | 57 ++ .../explainers/explainer-recursion.md | 176 ++++++ .../getting_started/_category_.json | 5 + .../barretenberg/_category_.json | 6 + .../getting_started/barretenberg/index.md | 47 ++ .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 145 +++++ .../hello_noir/project_breakdown.md | 159 +++++ .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 ++ .../installation/other_install_methods.md | 102 ++++ .../getting_started/tooling/noir_codegen.md | 114 ++++ .../version-v0.31.0/how_to/_category_.json | 5 + .../how_to/debugger/_category_.json | 6 + .../debugger/debugging_with_the_repl.md | 164 +++++ .../how_to/debugger/debugging_with_vs_code.md | 68 +++ .../version-v0.31.0/how_to/how-to-oracles.md | 273 +++++++++ .../how_to/how-to-recursion.md | 180 ++++++ .../how_to/how-to-solidity-verifier.md | 251 ++++++++ .../version-v0.31.0/how_to/merkle-proof.mdx | 48 ++ .../how_to/using-devcontainers.mdx | 110 ++++ docs/versioned_docs/version-v0.31.0/index.mdx | 67 ++ .../version-v0.31.0/migration_notes.md | 105 ++++ .../noir/concepts/_category_.json | 6 + .../version-v0.31.0/noir/concepts/assert.md | 45 ++ .../version-v0.31.0/noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 77 +++ .../version-v0.31.0/noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 253 ++++++++ .../noir/concepts/data_types/booleans.md | 28 + .../noir/concepts/data_types/fields.md | 192 ++++++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 110 ++++ .../noir/concepts/data_types/integers.md | 156 +++++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 193 ++++++ .../noir/concepts/data_types/strings.md | 79 +++ .../noir/concepts/data_types/structs.md | 70 +++ .../noir/concepts/data_types/tuples.md | 48 ++ .../noir/concepts/functions.md | 226 +++++++ .../version-v0.31.0/noir/concepts/generics.md | 106 ++++ .../version-v0.31.0/noir/concepts/globals.md | 72 +++ .../version-v0.31.0/noir/concepts/lambdas.md | 81 +++ .../noir/concepts/mutability.md | 121 ++++ .../version-v0.31.0/noir/concepts/ops.md | 98 +++ .../version-v0.31.0/noir/concepts/oracles.md | 31 + .../noir/concepts/shadowing.md | 44 ++ .../version-v0.31.0/noir/concepts/traits.md | 389 ++++++++++++ .../noir/concepts/unconstrained.md | 99 +++ .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 ++ .../modules_packages_crates/dependencies.md | 124 ++++ .../noir/modules_packages_crates/modules.md | 105 ++++ .../modules_packages_crates/workspaces.md | 42 ++ .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/bigint.md | 122 ++++ .../noir/standard_library/black_box_fns.md | 32 + .../noir/standard_library/bn254.md | 46 ++ .../standard_library/containers/boundedvec.md | 419 +++++++++++++ .../standard_library/containers/hashmap.md | 570 ++++++++++++++++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 170 ++++++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ciphers.mdx | 32 + .../cryptographic_primitives/ec_primitives.md | 102 ++++ .../ecdsa_sig_verification.mdx | 98 +++ .../cryptographic_primitives/eddsa.mdx | 37 ++ .../embedded_curve_ops.mdx | 98 +++ .../cryptographic_primitives/hashes.mdx | 246 ++++++++ .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/schnorr.mdx | 64 ++ .../noir/standard_library/is_unconstrained.md | 59 ++ .../noir/standard_library/logging.md | 78 +++ .../noir/standard_library/merkle_trees.md | 58 ++ .../noir/standard_library/options.md | 101 ++++ .../noir/standard_library/recursion.md | 85 +++ .../noir/standard_library/traits.md | 464 ++++++++++++++ .../noir/standard_library/zeroed.md | 26 + .../NoirJS/backend_barretenberg/.nojekyll | 1 + .../classes/BarretenbergBackend.md | 160 +++++ .../classes/BarretenbergVerifier.md | 58 ++ .../NoirJS/backend_barretenberg/index.md | 40 ++ .../type-aliases/BackendOptions.md | 21 + .../backend_barretenberg/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 52 ++ .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../NoirJS/noir_js/functions/keccak256.md | 21 + .../NoirJS/noir_js/functions/sha256.md | 21 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 49 ++ .../noir_js/type-aliases/ErrorWithPayload.md | 15 + .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 ++ .../noir_wasm/functions/compile_contract.md | 51 ++ .../noir_wasm/functions/createFileManager.md | 21 + .../functions/inflateDebugSymbols.md | 21 + .../reference/NoirJS/noir_wasm/index.md | 49 ++ .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + .../version-v0.31.0/reference/_category_.json | 5 + .../reference/debugger/_category_.json | 6 + .../debugger/debugger_known_limitations.md | 59 ++ .../reference/debugger/debugger_repl.md | 360 +++++++++++ .../reference/debugger/debugger_vscode.md | 82 +++ .../reference/nargo_commands.md | 244 ++++++++ .../version-v0.31.0/tooling/debugger.md | 26 + .../tooling/language_server.md | 43 ++ .../version-v0.31.0/tooling/testing.md | 62 ++ .../version-v0.31.0/tutorials/noirjs_app.md | 327 ++++++++++ .../version-v0.31.0-sidebars.json | 93 +++ tooling/noir_codegen/package.json | 2 +- tooling/noir_js/package.json | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- tooling/noir_js_types/package.json | 2 +- tooling/noirc_abi_wasm/package.json | 2 +- 139 files changed, 10451 insertions(+), 51 deletions(-) create mode 100644 docs/versioned_docs/version-v0.31.0/explainers/explainer-oracle.md create mode 100644 docs/versioned_docs/version-v0.31.0/explainers/explainer-recursion.md create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/installation/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/installation/other_install_methods.md create mode 100644 docs/versioned_docs/version-v0.31.0/getting_started/tooling/noir_codegen.md create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/debugger/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_the_repl.md create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_vs_code.md create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/how-to-oracles.md create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/how-to-recursion.md create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/how-to-solidity-verifier.md create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/merkle-proof.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/how_to/using-devcontainers.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/index.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/migration_notes.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/assert.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/comments.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/control_flow.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_bus.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/arrays.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/booleans.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/fields.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/function_types.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/integers.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/references.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/slices.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/strings.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/structs.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/tuples.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/functions.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/generics.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/globals.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/lambdas.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/mutability.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/ops.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/oracles.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/shadowing.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/traits.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/concepts/unconstrained.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/crates_and_packages.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/dependencies.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/modules.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/workspaces.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/bigint.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/black_box_fns.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/bn254.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/boundedvec.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/hashmap.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/vec.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ciphers.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/is_unconstrained.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/logging.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/merkle_trees.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/options.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/recursion.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/traits.md create mode 100644 docs/versioned_docs/version-v0.31.0/noir/standard_library/zeroed.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/.nojekyll create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/.nojekyll create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/and.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/keccak256.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/sha256.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/xor.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/index.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.31.0/reference/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/reference/debugger/_category_.json create mode 100644 docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_known_limitations.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_repl.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_vscode.md create mode 100644 docs/versioned_docs/version-v0.31.0/reference/nargo_commands.md create mode 100644 docs/versioned_docs/version-v0.31.0/tooling/debugger.md create mode 100644 docs/versioned_docs/version-v0.31.0/tooling/language_server.md create mode 100644 docs/versioned_docs/version-v0.31.0/tooling/testing.md create mode 100644 docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md create mode 100644 docs/versioned_sidebars/version-v0.31.0-sidebars.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 83338327d88..a30d3e16ba7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.30.0", - "acvm-repo": "0.46.0" + ".": "0.31.0", + "acvm-repo": "0.47.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index db4ec8f4567..30678c05fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,97 @@ # Changelog +## [0.31.0](https://github.com/noir-lang/noir/compare/v0.30.0...v0.31.0) (2024-06-17) + + +### ⚠ BREAKING CHANGES + +* remove `dep::` prefix ([#4946](https://github.com/noir-lang/noir/issues/4946)) +* remove `distinct` keyword ([#5219](https://github.com/noir-lang/noir/issues/5219)) +* remove `param_witnesses` and `return_witnesses` from ABI ([#5154](https://github.com/noir-lang/noir/issues/5154)) +* add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) +* restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) +* separate proving from `noir_js` ([#5072](https://github.com/noir-lang/noir/issues/5072)) +* switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) +* specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) +* **stdlib:** eddsa function using turbofish ([#5050](https://github.com/noir-lang/noir/issues/5050)) + +### Features + +* `pxe.addNullifiedNote(...)` (https://github.com/AztecProtocol/aztec-packages/pull/6948) ([7de19f5](https://github.com/noir-lang/noir/commit/7de19f5856591203271836f07154abae13f5102b)) +* Activate return_data in ACIR opcodes ([#5080](https://github.com/noir-lang/noir/issues/5080)) ([c9fda3c](https://github.com/noir-lang/noir/commit/c9fda3c7fd4575bfe7d457e8d4230e071f0129a0)) +* Add `as_witness` builtin function in order to constrain a witness to be equal to a variable ([#4641](https://github.com/noir-lang/noir/issues/4641)) ([faf5bd8](https://github.com/noir-lang/noir/commit/faf5bd8ed80fb89b4bb6a2536b9bfa9649579da7)) +* Add `set` and `set_unchecked` methods to `Vec` and `BoundedVec` ([#5241](https://github.com/noir-lang/noir/issues/5241)) ([1849389](https://github.com/noir-lang/noir/commit/1849389362e22e8236177f84b735dadf840cd637)) +* Add BoundedVec::map ([#5250](https://github.com/noir-lang/noir/issues/5250)) ([da1549c](https://github.com/noir-lang/noir/commit/da1549cfb296261b273a3a64908382e7b71512ad)) +* Add intrinsic to get if running inside an unconstrained context ([#5098](https://github.com/noir-lang/noir/issues/5098)) ([281ebf2](https://github.com/noir-lang/noir/commit/281ebf26e4cd16daf361938de505697f8d5fbd5e)) +* Add native rust implementation of schnorr signature verification ([#5053](https://github.com/noir-lang/noir/issues/5053)) ([fab1c35](https://github.com/noir-lang/noir/commit/fab1c3567d731ea7902635a7a020a8d14f94fd27)) +* Add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) ([14adafc](https://github.com/noir-lang/noir/commit/14adafc965fa9c833e096ec037e086aae67703ad)) +* Consider block parameters in variable liveness ([#5097](https://github.com/noir-lang/noir/issues/5097)) ([e4eb5f5](https://github.com/noir-lang/noir/commit/e4eb5f539f377fd3c2e1a874707ffce62a5bc10a)) +* **experimental:** Implement macro calls & splicing into `Expr` values ([#5203](https://github.com/noir-lang/noir/issues/5203)) ([d9b4712](https://github.com/noir-lang/noir/commit/d9b4712bf1a62548dd7ed17b181882ae537d70dd)) +* Implement println in the comptime interpreter ([#5197](https://github.com/noir-lang/noir/issues/5197)) ([7f08343](https://github.com/noir-lang/noir/commit/7f08343dfcafddfcec1b238746a69273ae4f4e2b)) +* Implement turbofish operator ([#3542](https://github.com/noir-lang/noir/issues/3542)) ([226724e](https://github.com/noir-lang/noir/commit/226724e3b54c2e0d9ba005661c76b40a87d9295a)) +* Make ACVM generic across fields ([#5114](https://github.com/noir-lang/noir/issues/5114)) ([70f374c](https://github.com/noir-lang/noir/commit/70f374c06642962d8f2b95b80f8c938fcf7761d7)) +* Move abi demonomorphizer to noir_codegen and use noir_codegen in protocol types (https://github.com/AztecProtocol/aztec-packages/pull/6302) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Move to_radix to a blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6294) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* **nargo:** Hidden option to show contract artifact paths written by `nargo compile` (https://github.com/AztecProtocol/aztec-packages/pull/6131) ([ff67e14](https://github.com/noir-lang/noir/commit/ff67e145d086bf6fdf58fb5e57927033e52e03d3)) +* Place return value witnesses directly after function arguments ([#5142](https://github.com/noir-lang/noir/issues/5142)) ([1252b5f](https://github.com/noir-lang/noir/commit/1252b5fcc7ed56bb55e95745b83be6e556805397)) +* Private Kernel Recursion (https://github.com/AztecProtocol/aztec-packages/pull/6278) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Proper padding in ts AES and constrained AES in body and header computations (https://github.com/AztecProtocol/aztec-packages/pull/6269) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Remove `dep::` prefix ([#4946](https://github.com/noir-lang/noir/issues/4946)) ([d6d0ae2](https://github.com/noir-lang/noir/commit/d6d0ae26d2fef083dc240539b834d934c84b0326)) +* Remove conditional compilation of `bn254_blackbox_solver` ([#5058](https://github.com/noir-lang/noir/issues/5058)) ([9420d7c](https://github.com/noir-lang/noir/commit/9420d7c2ba6bbbf5ecb9a066837c505310955b6c)) +* Remove external blackbox solver from acir simulator (https://github.com/AztecProtocol/aztec-packages/pull/6586) ([a40a9a5](https://github.com/noir-lang/noir/commit/a40a9a55571deed386688fb84260bdf2794d4d38)) +* Replace stdlib poseidon implementation with optimized version ([#5122](https://github.com/noir-lang/noir/issues/5122)) ([11e98f3](https://github.com/noir-lang/noir/commit/11e98f348d1d43a9b28d83ec3308027b7afc0da6)) +* Restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) ([bdb2bc6](https://github.com/noir-lang/noir/commit/bdb2bc608ea8fd52d46545a38b68dd2558b28110)) +* Separate proving from `noir_js` ([#5072](https://github.com/noir-lang/noir/issues/5072)) ([c93c738](https://github.com/noir-lang/noir/commit/c93c7380c705fcec5c77bfc436c2f5ea085edd77)) +* Separate runtimes of SSA functions before inlining ([#5121](https://github.com/noir-lang/noir/issues/5121)) ([69eca9b](https://github.com/noir-lang/noir/commit/69eca9b8671fa54192bef814dd584fdb5387a5f7)) +* Specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Standardize pedersen functions to return `EmbeddedCurvePoint` ([#5190](https://github.com/noir-lang/noir/issues/5190)) ([3b85b36](https://github.com/noir-lang/noir/commit/3b85b3637f81f3894a7faa07fd299f9d64747214)) +* **stdlib:** Eddsa function using turbofish ([#5050](https://github.com/noir-lang/noir/issues/5050)) ([7936262](https://github.com/noir-lang/noir/commit/79362629ed8cf42b6601e9a551ed8f9fe03e0112)) +* Support casting in globals ([#5164](https://github.com/noir-lang/noir/issues/5164)) ([6d3e732](https://github.com/noir-lang/noir/commit/6d3e732e06033b53506656acdd3d7759bd27f106)) +* Switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6280) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6332) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6573) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6986) ([7de19f5](https://github.com/noir-lang/noir/commit/7de19f5856591203271836f07154abae13f5102b)) +* ToRadix BB + avm transpiler support (https://github.com/AztecProtocol/aztec-packages/pull/6330) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) + + +### Bug Fixes + +* Add support for nested arrays returned by oracles ([#5132](https://github.com/noir-lang/noir/issues/5132)) ([f846879](https://github.com/noir-lang/noir/commit/f846879dd038328bd0a1d39a72b448ef52a1002b)) +* Apply self type from generic trait constraint before instantiating identifiers ([#5087](https://github.com/noir-lang/noir/issues/5087)) ([2b4755c](https://github.com/noir-lang/noir/commit/2b4755c2b57460d5eb839ee835f8c9acd5773a7c)) +* Auto dereference trait methods in the elaborator ([#5124](https://github.com/noir-lang/noir/issues/5124)) ([56c1a85](https://github.com/noir-lang/noir/commit/56c1a85056ed338644595f1aa58cc94563786b9e)) +* Check for public args in aztec functions (https://github.com/AztecProtocol/aztec-packages/pull/6355) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Disable `if` optimization ([#5240](https://github.com/noir-lang/noir/issues/5240)) ([a2816db](https://github.com/noir-lang/noir/commit/a2816dbf7f9d31967fc95205a43fdfdf181029b0)) +* **elaborator:** Fix duplicate methods error ([#5225](https://github.com/noir-lang/noir/issues/5225)) ([87a1d8e](https://github.com/noir-lang/noir/commit/87a1d8ebaadb5f0f1ed637b96816f971f946af87)) +* **elaborator:** Fix regression introduced by lazy-global changes ([#5223](https://github.com/noir-lang/noir/issues/5223)) ([fde432a](https://github.com/noir-lang/noir/commit/fde432aacc436b6c57f0d937d7c86836bac0b465)) +* **elaborator:** Invert unconstrained check ([#5176](https://github.com/noir-lang/noir/issues/5176)) ([967c0fa](https://github.com/noir-lang/noir/commit/967c0fa76da9384afe918a8b23eef60f12f29292)) +* **elaborator:** Lazily elaborate globals ([#5191](https://github.com/noir-lang/noir/issues/5191)) ([9c99a97](https://github.com/noir-lang/noir/commit/9c99a97ca8f42bee23cf97ebd724fdc51e647c60)) +* Error for allocate instructions in acir-gen ([#5200](https://github.com/noir-lang/noir/issues/5200)) ([58c7532](https://github.com/noir-lang/noir/commit/58c7532da8dd86ee02b20d7e7809f5437f667845)) +* **experimental elaborator:** Avoid calling `add_generics` twice on trait methods ([#5108](https://github.com/noir-lang/noir/issues/5108)) ([7d8c0a3](https://github.com/noir-lang/noir/commit/7d8c0a3a1ae143b574b2fa62cae7c0a493005c70)) +* **experimental elaborator:** Clear generics after elaborating type aliases ([#5136](https://github.com/noir-lang/noir/issues/5136)) ([b0a7d0b](https://github.com/noir-lang/noir/commit/b0a7d0b12328d3ed9faed87b78792b77786018e0)) +* **experimental elaborator:** Fix `impl Trait` when `--use-elaborator` is selected ([#5138](https://github.com/noir-lang/noir/issues/5138)) ([7ea5962](https://github.com/noir-lang/noir/commit/7ea5962e77b7183374a4e14da3a237ccd63f00a0)) +* **experimental elaborator:** Fix definition kind of globals and tuple patterns with `--use-elaborator` flag ([#5139](https://github.com/noir-lang/noir/issues/5139)) ([a140dec](https://github.com/noir-lang/noir/commit/a140dec4580459c5856d44337de3ea08aa7fb44a)) +* **experimental elaborator:** Fix duplicate `resolve_type` on self type and don't leak a trait impl's generics ([#5102](https://github.com/noir-lang/noir/issues/5102)) ([db561e2](https://github.com/noir-lang/noir/commit/db561e229cfcb35f23205cbb7e41fcf5ece68ee5)) +* **experimental elaborator:** Fix frontend tests when `--use-elaborator` flag is specified ([#5145](https://github.com/noir-lang/noir/issues/5145)) ([d6122eb](https://github.com/noir-lang/noir/commit/d6122eb9e88aa2b1bb6c990e452fa9678ae49704)) +* **experimental elaborator:** Fix global values used in the elaborator ([#5135](https://github.com/noir-lang/noir/issues/5135)) ([e73cdbb](https://github.com/noir-lang/noir/commit/e73cdbb93b0714331fef754f862d89c08c28a9e5)) +* **experimental elaborator:** Fix globals which use function calls ([#5172](https://github.com/noir-lang/noir/issues/5172)) ([ab0b1a8](https://github.com/noir-lang/noir/commit/ab0b1a85cc91f8ed748ee393ece54f5c3b43d7ef)) +* **experimental elaborator:** Fix panic in the elaborator ([#5082](https://github.com/noir-lang/noir/issues/5082)) ([ffcb410](https://github.com/noir-lang/noir/commit/ffcb410978a362c73783fbfe5bbdc9691499609e)) +* **experimental elaborator:** Only call `add_generics` once ([#5091](https://github.com/noir-lang/noir/issues/5091)) ([f5d2946](https://github.com/noir-lang/noir/commit/f5d294645e82fc85d8dc28ee2a846ba11af85ce5)) +* Fix panic in `get_global_let_statement` ([#5177](https://github.com/noir-lang/noir/issues/5177)) ([b769b01](https://github.com/noir-lang/noir/commit/b769b01fd06a6a2c66c72f9aa4e1d346b0fca123)) +* **frontend:** Call trait method with mut self from generic definition ([#5041](https://github.com/noir-lang/noir/issues/5041)) ([89846cf](https://github.com/noir-lang/noir/commit/89846cfbc4961c5258d91b5973f027be80885a20)) +* **frontend:** Correctly monomorphize turbofish functions ([#5049](https://github.com/noir-lang/noir/issues/5049)) ([fd772e7](https://github.com/noir-lang/noir/commit/fd772e7a764004373f5a41a54eb6847f4decda77)) +* **frontend:** Resolve object types from method calls a single time ([#5131](https://github.com/noir-lang/noir/issues/5131)) ([3afe023](https://github.com/noir-lang/noir/commit/3afe023543e301aafaf2b79f0ccd6d7936dd53a9)) +* Temporarily revert to_radix blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6304) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Use plain integer addresses for opcodes in DAP disassembly view ([#4941](https://github.com/noir-lang/noir/issues/4941)) ([d43ba1b](https://github.com/noir-lang/noir/commit/d43ba1bddbf6ebd56a7bee0e1db38d155fec95d5)) +* Use predicate for curve operations ([#5076](https://github.com/noir-lang/noir/issues/5076)) ([145b909](https://github.com/noir-lang/noir/commit/145b90945486907cb6db75d3f3f93a58d19b2a32)) +* Wrapping in signed division ([#5134](https://github.com/noir-lang/noir/issues/5134)) ([29baeb4](https://github.com/noir-lang/noir/commit/29baeb41e15918935c437e0a2759c6b936f125a4)) + + +### Miscellaneous Chores + +* Remove `distinct` keyword ([#5219](https://github.com/noir-lang/noir/issues/5219)) ([1d62c59](https://github.com/noir-lang/noir/commit/1d62c59a8f02f7d277c5bf9ed637348a3b2f399c)) +* Remove `param_witnesses` and `return_witnesses` from ABI ([#5154](https://github.com/noir-lang/noir/issues/5154)) ([21562ae](https://github.com/noir-lang/noir/commit/21562aeea162d246573967115e7c519715f6d3d8)) + ## [0.30.0](https://github.com/noir-lang/noir/compare/v0.29.0...v0.30.0) (2024-05-20) diff --git a/Cargo.lock b/Cargo.lock index 7b96737f241..9a54944933e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acir_field", "base64 0.21.2", @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.46.0" +version = "0.47.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -40,7 +40,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -55,7 +55,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acir", "blake2", @@ -93,7 +93,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -443,7 +443,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aztec_macros" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "convert_case 0.6.0", @@ -570,7 +570,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -588,7 +588,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acir_field", "serde", @@ -596,7 +596,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.46.0" +version = "0.47.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1514,7 +1514,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.30.0" +version = "0.31.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -2075,7 +2075,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.30.0" +version = "0.31.0" [[package]] name = "itertools" @@ -2470,7 +2470,7 @@ dependencies = [ [[package]] name = "nargo" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "codespan-reporting", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "assert_cmd", @@ -2548,7 +2548,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.30.0" +version = "0.31.0" dependencies = [ "bytecount", "noirc_frontend", @@ -2560,7 +2560,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.30.0" +version = "0.31.0" dependencies = [ "dirs", "fm", @@ -2639,7 +2639,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "assert_cmd", @@ -2674,7 +2674,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "async-lsp", @@ -2700,7 +2700,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "build-data", @@ -2723,7 +2723,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "iter-extended", @@ -2742,7 +2742,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "build-data", @@ -2759,11 +2759,11 @@ dependencies = [ [[package]] name = "noirc_arena" -version = "0.30.0" +version = "0.31.0" [[package]] name = "noirc_driver" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "aztec_macros", @@ -2783,7 +2783,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "base64 0.21.2", @@ -2801,7 +2801,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "chrono", @@ -2818,7 +2818,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "base64 0.21.2", @@ -2850,7 +2850,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.30.0" +version = "0.31.0" dependencies = [ "acvm", "iter-extended", diff --git a/Cargo.toml b/Cargo.toml index 639807819ab..8a4c94d8876 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.30.0" +version = "0.31.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -50,13 +50,13 @@ repository = "https://github.com/noir-lang/noir/" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "0.46.0", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "0.46.0", path = "acvm-repo/acir", default-features = false } -acvm = { version = "0.46.0", path = "acvm-repo/acvm" } -brillig = { version = "0.46.0", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "0.46.0", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "0.46.0", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "0.46.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +acir_field = { version = "0.47.0", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "0.47.0", path = "acvm-repo/acir", default-features = false } +acvm = { version = "0.47.0", path = "acvm-repo/acvm" } +brillig = { version = "0.47.0", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "0.47.0", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "0.47.0", path = "acvm-repo/blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "0.47.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies fm = { path = "compiler/fm" } diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md index c9bb0d610eb..4db36aadec9 100644 --- a/acvm-repo/CHANGELOG.md +++ b/acvm-repo/CHANGELOG.md @@ -5,6 +5,154 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.47.0](https://github.com/noir-lang/noir/compare/v0.46.0...v0.47.0) (2024-06-17) + + +### ⚠ BREAKING CHANGES + +* add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) +* restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) +* switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) +* specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) +* remove `Opcode::Brillig` from ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5995) +* AES blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6016) +* Bit shift is restricted to u8 right operand ([#4907](https://github.com/noir-lang/noir/issues/4907)) +* contract interfaces and better function calls (https://github.com/AztecProtocol/aztec-packages/pull/5687) +* change backend width to 4 (https://github.com/AztecProtocol/aztec-packages/pull/5374) +* Use fixed size arrays in black box functions where sizes are known (https://github.com/AztecProtocol/aztec-packages/pull/5620) +* trap with revert data (https://github.com/AztecProtocol/aztec-packages/pull/5732) +* **acir:** BrilligCall opcode (https://github.com/AztecProtocol/aztec-packages/pull/5709) +* remove fixed-length keccak256 (https://github.com/AztecProtocol/aztec-packages/pull/5617) +* storage_layout and `#[aztec(storage)]` (https://github.com/AztecProtocol/aztec-packages/pull/5387) +* **acir:** Add predicate to call opcode (https://github.com/AztecProtocol/aztec-packages/pull/5616) +* contract_abi-exports (https://github.com/AztecProtocol/aztec-packages/pull/5386) +* Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) +* automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) +* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) +* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) +* rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) +* init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) + +### Features + +* `multi_scalar_mul` blackbox func (https://github.com/AztecProtocol/aztec-packages/pull/6097) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* `variable_base_scalar_mul` blackbox func (https://github.com/AztecProtocol/aztec-packages/pull/6039) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **acir_gen:** Brillig stdlib ([#4848](https://github.com/noir-lang/noir/issues/4848)) ([0c8175c](https://github.com/noir-lang/noir/commit/0c8175cb539efd9427c73ae5af0d48abe688ebab)) +* **acir_gen:** Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) +* **acir:** Add predicate to call opcode (https://github.com/AztecProtocol/aztec-packages/pull/5616) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* **acir:** BrilligCall opcode (https://github.com/AztecProtocol/aztec-packages/pull/5709) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Activate return_data in ACIR opcodes ([#5080](https://github.com/noir-lang/noir/issues/5080)) ([c9fda3c](https://github.com/noir-lang/noir/commit/c9fda3c7fd4575bfe7d457e8d4230e071f0129a0)) +* **acvm_js:** Execute program ([#4694](https://github.com/noir-lang/noir/issues/4694)) ([386f6d0](https://github.com/noir-lang/noir/commit/386f6d0a5822912db878285cb001032a7c0ff622)) +* **acvm:** Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) +* Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) +* Add native rust implementation of schnorr signature verification ([#5053](https://github.com/noir-lang/noir/issues/5053)) ([fab1c35](https://github.com/noir-lang/noir/commit/fab1c3567d731ea7902635a7a020a8d14f94fd27)) +* Add native rust implementations of pedersen functions ([#4871](https://github.com/noir-lang/noir/issues/4871)) ([fb039f7](https://github.com/noir-lang/noir/commit/fb039f74df23aea39bc0593a5d538d82b4efadf0)) +* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) +* Add return values to aztec fns (https://github.com/AztecProtocol/aztec-packages/pull/5389) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* Add session id to foreign call RPC requests ([#5205](https://github.com/noir-lang/noir/issues/5205)) ([14adafc](https://github.com/noir-lang/noir/commit/14adafc965fa9c833e096ec037e086aae67703ad)) +* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* AES blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6016) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) +* Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **avm:** Integrate AVM with initializers (https://github.com/AztecProtocol/aztec-packages/pull/5469) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) +* Bit shift is restricted to u8 right operand ([#4907](https://github.com/noir-lang/noir/issues/4907)) ([c4b0369](https://github.com/noir-lang/noir/commit/c4b03691feca17ef268acab523292f3051f672ea)) +* Brillig heterogeneous memory cells (https://github.com/AztecProtocol/aztec-packages/pull/5608) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) +* Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Brillig pointer codegen and execution (https://github.com/AztecProtocol/aztec-packages/pull/5737) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ([0bc18c4](https://github.com/noir-lang/noir/commit/0bc18c4f78171590dd58bded959f68f53a44cc8c)) +* Change backend width to 4 (https://github.com/AztecProtocol/aztec-packages/pull/5374) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Contract interfaces and better function calls (https://github.com/AztecProtocol/aztec-packages/pull/5687) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Contract_abi-exports (https://github.com/AztecProtocol/aztec-packages/pull/5386) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* Dynamic assertion payloads v2 (https://github.com/AztecProtocol/aztec-packages/pull/5949) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) +* Handle `BrilligCall` opcodes in the debugger ([#4897](https://github.com/noir-lang/noir/issues/4897)) ([b380dc4](https://github.com/noir-lang/noir/commit/b380dc44de5c9f8de278ece3d531ebbc2c9238ba)) +* Impl of missing functionality in new key store (https://github.com/AztecProtocol/aztec-packages/pull/5750) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Increase default expression width to 4 ([#4995](https://github.com/noir-lang/noir/issues/4995)) ([f01d309](https://github.com/noir-lang/noir/commit/f01d3090759a5ff0f1f83c5616d22890c6bd76be)) +* Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Make ACVM generic across fields ([#5114](https://github.com/noir-lang/noir/issues/5114)) ([70f374c](https://github.com/noir-lang/noir/commit/70f374c06642962d8f2b95b80f8c938fcf7761d7)) +* Move abi demonomorphizer to noir_codegen and use noir_codegen in protocol types (https://github.com/AztecProtocol/aztec-packages/pull/6302) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Move to_radix to a blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6294) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* **nargo:** Handle call stacks for multiple Acir calls ([#4711](https://github.com/noir-lang/noir/issues/4711)) ([5b23171](https://github.com/noir-lang/noir/commit/5b231714740447d82cde7cdbe65d4a8b46a31df4)) +* **nargo:** Hidden option to show contract artifact paths written by `nargo compile` (https://github.com/AztecProtocol/aztec-packages/pull/6131) ([ff67e14](https://github.com/noir-lang/noir/commit/ff67e145d086bf6fdf58fb5e57927033e52e03d3)) +* New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Parsing non-string assertion payloads in noir js (https://github.com/AztecProtocol/aztec-packages/pull/6079) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* Private Kernel Recursion (https://github.com/AztecProtocol/aztec-packages/pull/6278) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Proper padding in ts AES and constrained AES in body and header computations (https://github.com/AztecProtocol/aztec-packages/pull/6269) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Remove conditional compilation of `bn254_blackbox_solver` ([#5058](https://github.com/noir-lang/noir/issues/5058)) ([9420d7c](https://github.com/noir-lang/noir/commit/9420d7c2ba6bbbf5ecb9a066837c505310955b6c)) +* Remove external blackbox solver from acir simulator (https://github.com/AztecProtocol/aztec-packages/pull/6586) ([a40a9a5](https://github.com/noir-lang/noir/commit/a40a9a55571deed386688fb84260bdf2794d4d38)) +* Restore hashing args via slice for performance (https://github.com/AztecProtocol/aztec-packages/pull/5539) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* Restrict noir word size to u32 ([#5180](https://github.com/noir-lang/noir/issues/5180)) ([bdb2bc6](https://github.com/noir-lang/noir/commit/bdb2bc608ea8fd52d46545a38b68dd2558b28110)) +* Separate runtimes of SSA functions before inlining ([#5121](https://github.com/noir-lang/noir/issues/5121)) ([69eca9b](https://github.com/noir-lang/noir/commit/69eca9b8671fa54192bef814dd584fdb5387a5f7)) +* Set aztec private functions to be recursive (https://github.com/AztecProtocol/aztec-packages/pull/6192) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **simulator:** Fetch return values at circuit execution (https://github.com/AztecProtocol/aztec-packages/pull/5642) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) +* Specify databus arrays for BB (https://github.com/AztecProtocol/aztec-packages/pull/6239) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Storage_layout and `#[aztec(storage)]` (https://github.com/AztecProtocol/aztec-packages/pull/5387) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Switch `bb` over to read ACIR from nargo artifacts (https://github.com/AztecProtocol/aztec-packages/pull/6283) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5572) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5619) ([2bd006a](https://github.com/noir-lang/noir/commit/2bd006ae07499e8702b0fa9565855f0a5ef1a589)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5697) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5794) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5814) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5935) ([1b867b1](https://github.com/noir-lang/noir/commit/1b867b121fba5db3087ca845b4934e6732b23fd1)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5955) ([1b867b1](https://github.com/noir-lang/noir/commit/1b867b121fba5db3087ca845b4934e6732b23fd1)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5999) ([1b867b1](https://github.com/noir-lang/noir/commit/1b867b121fba5db3087ca845b4934e6732b23fd1)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6280) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6332) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/6573) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* ToRadix BB + avm transpiler support (https://github.com/AztecProtocol/aztec-packages/pull/6330) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Trap with revert data (https://github.com/AztecProtocol/aztec-packages/pull/5732) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Use fixed size arrays in black box functions where sizes are known (https://github.com/AztecProtocol/aztec-packages/pull/5620) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Variable length returns (https://github.com/AztecProtocol/aztec-packages/pull/5633) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) + + +### Bug Fixes + +* **acvm:** Mark outputs of Opcode::Call solvable ([#4708](https://github.com/noir-lang/noir/issues/4708)) ([8fea405](https://github.com/noir-lang/noir/commit/8fea40576f262bd5bb588923c0660d8967404e56)) +* Add support for nested arrays returned by oracles ([#5132](https://github.com/noir-lang/noir/issues/5132)) ([f846879](https://github.com/noir-lang/noir/commit/f846879dd038328bd0a1d39a72b448ef52a1002b)) +* Avoid huge unrolling in hash_args (https://github.com/AztecProtocol/aztec-packages/pull/5703) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) +* Catch panics from EC point creation (e.g. the point is at infinity) ([#4790](https://github.com/noir-lang/noir/issues/4790)) ([645dba1](https://github.com/noir-lang/noir/commit/645dba192f16ef34018828186ffb297422a8dc73)) +* Check for public args in aztec functions (https://github.com/AztecProtocol/aztec-packages/pull/6355) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) +* Don't reuse brillig with slice arguments (https://github.com/AztecProtocol/aztec-packages/pull/5800) ([0f9ae0a](https://github.com/noir-lang/noir/commit/0f9ae0ac1d68714b56ba4524aedcc67212494f1b)) +* Issue 4682 and add solver for unconstrained bigintegers ([#4729](https://github.com/noir-lang/noir/issues/4729)) ([e4d33c1](https://github.com/noir-lang/noir/commit/e4d33c126a2795d9aaa6048d4e91b64cb4bbe4f2)) +* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Proper field inversion for bigints ([#4802](https://github.com/noir-lang/noir/issues/4802)) ([b46d0e3](https://github.com/noir-lang/noir/commit/b46d0e39f4252f8bbaa987f88d112e4c233b3d61)) +* Temporarily revert to_radix blackbox (https://github.com/AztecProtocol/aztec-packages/pull/6304) ([436bbda](https://github.com/noir-lang/noir/commit/436bbdaadb2a294b94f93e53d7d3cad3859c7e46)) + + +### Miscellaneous Chores + +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove `Opcode::Brillig` from ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5995) ([73a635e](https://github.com/noir-lang/noir/commit/73a635e5086cf3407f9846ce39807cd15b4e485a)) +* Remove fixed-length keccak256 (https://github.com/AztecProtocol/aztec-packages/pull/5617) ([305bcdc](https://github.com/noir-lang/noir/commit/305bcdcbd01cb84dbaac900f14cb6cf867f83bda)) +* Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) + ## [0.46.0](https://github.com/noir-lang/noir/compare/v0.45.0...v0.46.0) (2024-05-20) diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml index 101ce7a0f39..7a8f10c98ef 100644 --- a/acvm-repo/acir/Cargo.toml +++ b/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index 801935c90f9..303d7b6471f 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index 59305ec49f0..892575902a4 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 63f64e97729..e457b6391ab 100644 --- a/acvm-repo/acvm_js/Cargo.toml +++ b/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index 48c5b2a8644..6085bc0563e 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "0.46.0", + "version": "0.47.0", "publishConfig": { "access": "public" }, diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index dd7f84e63e4..06bd3ceabef 100644 --- a/acvm-repo/blackbox_solver/Cargo.toml +++ b/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index 37d40de71a9..cc2d15aaa86 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml index 245767dcecd..7c1965c8f3e 100644 --- a/acvm-repo/brillig/Cargo.toml +++ b/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 4735514c9a7..d048d625083 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "0.46.0" +version = "0.47.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index 0bb9b803ee0..49956d79882 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.30.0", + "version": "0.31.0", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/docs/versioned_docs/version-v0.31.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.31.0/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.31.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.31.0/explainers/explainer-recursion.md new file mode 100644 index 00000000000..18846176ca7 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.31.0/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json b/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json new file mode 100644 index 00000000000..27a8e89228d --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 1, + "label": "Install Barretenberg", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md b/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md new file mode 100644 index 00000000000..0102c86770b --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md @@ -0,0 +1,47 @@ +--- +title: Barretenberg Installation +description: bb is a command line tool for interacting with Aztec's proving backend Barretenberg. This page is a quick guide on how to install `bb` +keywords: [ + Barretenberg + bb + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches +] +pagination_next: getting_started/hello_noir/index +--- + +`bb` is the CLI tool for generating and verifying proofs for Noir programs using the Barretenberg proving library. It also allows generating solidity verifier contracts for which you can verify contracts which were constructed using `bb`. + +## Installing `bb` + +Open a terminal on your machine, and write: + +##### macOS (Apple Silicon) + +```bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash +source ~/.zshrc +bbup -v 0.41.0 +``` + +##### macOS (Intel) + +```bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash +source ~/.zshrc +bbup -v 0.41.0 +``` + +##### Linux (Bash) + +```bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash +source ~/.bashrc +bbup -v 0.41.0 +``` + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..976a2325de0 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..1ade3f09ae3 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md @@ -0,0 +1,145 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. + +## Execute Our Noir Program + +Now that the project is set up, we can execute our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Execute your Noir program: + +```sh +nargo execute witness-name +``` + +The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. + +## Prove Our Noir Program + +:::info + +Nargo no longer handles communicating with backends in order to generate proofs. In order to prove/verify your Noir programs, you'll need an installation of [bb](../barretenberg/index.md). + +::: + +Prove the valid execution of your Noir program using `bb`: + +```sh +bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./proof +``` + +A new file called `proof` will be generated in your project directory, containing the generated proof for your program. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. + +Verify your proof by running: + +```sh +bb write_vk -b ./target/hello_world.json -o ./target/vk +bb verify -k ./target/vk -p ./proof +``` + +The verification will complete in silence if it is successful. If it fails, it will log the corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..29688df148f --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,159 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML + file, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is constrained by the proof of the execution of said program (i.e. if the condition was not met, the verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply the inputs to the Noir program (both private and public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. + +If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for execution by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: + +```bash +nargo execute foo +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: + +```bash +nargo execute -p OtherProver bar +``` + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.31.0/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.31.0/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..3634723562b --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/installation/other_install_methods.md @@ -0,0 +1,102 @@ +--- +title: Alternative Installations +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Uninstalling Nargo + ] +sidebar_position: 1 +--- + +## Encouraged Installation Method: Noirup + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + ```sh + noirup --path ./path/to/local/source + ``` + +## Installation on Windows + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#encouraged-installation-method-noirup). + +## Uninstalling Nargo + +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` diff --git a/docs/versioned_docs/version-v0.31.0/getting_started/tooling/noir_codegen.md b/docs/versioned_docs/version-v0.31.0/getting_started/tooling/noir_codegen.md new file mode 100644 index 00000000000..f7505bef7ab --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/getting_started/tooling/noir_codegen.md @@ -0,0 +1,114 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 3 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +yarn add @noir-lang/noir_codegen -D +``` + +### Nargo library +Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = +{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.31.0/how_to/_category_.json b/docs/versioned_docs/version-v0.31.0/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.31.0/how_to/debugger/_category_.json new file mode 100644 index 00000000000..cc2cbb1c253 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugging", + "position": 5, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_the_repl.md new file mode 100644 index 00000000000..09e5bae68ad --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_the_repl.md @@ -0,0 +1,164 @@ +--- +title: Using the REPL Debugger +description: + Step by step guide on how to debug your Noir circuits with the REPL Debugger. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +#### Pre-requisites + +In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. + +## Debugging a simple circuit + +Let's debug a simple circuit: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: + +`$ nargo debug` + +You should be seeing this in your terminal: + +``` +[main] Starting debugger +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> +``` + +The debugger displays the current Noir code location, and it is now waiting for us to drive it. + +Let's first take a look at the available commands. For that we'll use the `help` command. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: + +``` +> memory +Unconstrained VM memory not available +> +``` + +Before continuing, we can take a look at the initial witness map: + +``` +> witness +_0 = 1 +_1 = 2 +> +``` + +Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: + +``` +> witness +_0 = 1 +_1 = 2 +> witness 1 3 +_1 = 3 +> witness +_0 = 1 +_1 = 3 +> witness 1 2 +_1 = 2 +> witness +_0 = 1 +_1 = 2 +> +``` + +Now we can inspect the current state of local variables. For that we use the `vars` command. + +``` +> vars +> +``` + +We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. + +``` +> vars +> next +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> vars +x:Field = 0x01 +``` + +As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. + +``` +> next + 1 fn main(x : Field, y : pub Field) { + 2 -> assert(x != y); + 3 } +> vars +y:Field = 0x02 +x:Field = 0x01 +``` + +Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. + +Let's continue to the end: + +``` +> continue +(Continuing execution...) +Finished execution +> q +[main] Circuit witness successfully solved +``` + +Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. + +We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_vs_code.md new file mode 100644 index 00000000000..a5858c1a5eb --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/debugger/debugging_with_vs_code.md @@ -0,0 +1,68 @@ +--- +title: Using the VS Code Debugger +description: + Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. + +#### Pre-requisites + +- Nargo +- vscode-noir +- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). + +## Running the debugger + +The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. + +You should see something like this: + +![Debugger launched](@site/static/img/debugger/1-started.png) + +Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: + +![Debug pane icon](@site/static/img/debugger/2-icon.png) + +You will now see two categories of variables: Locals and Witness Map. + +![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) + +1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. + +2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. + +Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. + +You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. + +Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. + +![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) + +Now we can see in the variables pane that there's values for `digest`, `result` and `x`. + +![Inspecting locals](@site/static/img/debugger/5-assert.png) + +We can also inspect the values of variables by directly hovering on them on the code. + +![Hover locals](@site/static/img/debugger/6-hover.png) + +Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. + +We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). + +![Breakpoint](@site/static/img/debugger/7-break.png) + +Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. + +That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.31.0/how_to/how-to-oracles.md new file mode 100644 index 00000000000..2d2ed5c94b9 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/how-to-oracles.md @@ -0,0 +1,273 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("resolve_function_call", async (params) => { + if params.function !== "getSqrt" { + throw Error("Unexpected foreign call") + }; + const values = params.inputs[0].Array.map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; + }); + return { values: [{ Array: values }] }; +}); +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface SingleForeignCallParam { + Single: string, +} + +interface ArrayForeignCallParam { + Array: string[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: Multidimensional Arrays + +If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in json as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.execute(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => i.toString("hex")) }, + ]); + return [oracleReturn.values[0].Array]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/docs/versioned_docs/version-v0.31.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.31.0/how_to/how-to-recursion.md new file mode 100644 index 00000000000..aac84e29fac --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/how-to-recursion.md @@ -0,0 +1,180 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main), + recursive: new Noir(circuits.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) +const recursiveProof = await backends.recursive.generateProof(recursiveWitness); +``` + +::: diff --git a/docs/versioned_docs/version-v0.31.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.31.0/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e6ed9abaec6 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/how-to-solidity-verifier.md @@ -0,0 +1,251 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo compile +``` + +This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: + +```sh +# Here we pass the path to the newly generated Noir artifact. +bb write_vk -b ./target/.json +bb contract +``` + +replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity +file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +
Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. + +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the + +```bash +# This value must be changed to match the number of public inputs (including return values!) in your program. +NUM_PUBLIC_INPUTS=1 +PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS +HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') +HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') + +echo "Public inputs:" +echo $HEX_PUBLIC_INPUTS + +echo "Proof:" +echo "0x$HEX_PROOF" +``` + +Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.31.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.31.0/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..0a128adb2de --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +sidebar_position: 4 +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message.as_slice()); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message.as_slice()); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/docs/versioned_docs/version-v0.31.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.31.0/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.31.0/index.mdx b/docs/versioned_docs/version-v0.31.0/index.mdx new file mode 100644 index 00000000000..a6bd306f91d --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.31.0/migration_notes.md b/docs/versioned_docs/version-v0.31.0/migration_notes.md new file mode 100644 index 00000000000..6bd740024e5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.31.0/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/assert.md new file mode 100644 index 00000000000..bcff613a695 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/assert.md @@ -0,0 +1,45 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/control_flow.md new file mode 100644 index 00000000000..045d3c3a5f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/control_flow.md @@ -0,0 +1,77 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_bus.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..95d749053e2 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/arrays.md @@ -0,0 +1,253 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` + +However, multidimensional slices are not supported. For example, the following code will error at compile time: + +```rust +let slice : [[Field]] = &[]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..2507af710e7 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/booleans.md @@ -0,0 +1,28 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and +[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..a10a4810788 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/fields.md @@ -0,0 +1,192 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..357813c147a --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/index.md @@ -0,0 +1,110 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](../generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..a1d59bf3166 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/integers.md @@ -0,0 +1,156 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. +The Noir frontend supports both unsigned and signed integer types. +The allowed sizes are 1, 8, 16, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let x = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo execute +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..dff08d63ffb --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/slices.mdx @@ -0,0 +1,193 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +fn main() -> pub u32 { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = &[]; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = &[1, 2].append(&[3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` + +### as_array + +Converts this slice into an array. + +Make sure to specify the size of the resulting array. +Panics if the resulting array length is different than the slice's length. + +```rust +fn as_array(self) -> [T; N] +``` + +Example: + +```rust +fn main() { + let slice = &[5, 6]; + + // Always specify the length of the resulting array! + let array: [Field; 2] = slice.as_array(); + + assert(array[0] == slice[0]); + assert(array[1] == slice[1]); +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..1fdee42425e --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/strings.md @@ -0,0 +1,79 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging.md). + +```rust + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/functions.md new file mode 100644 index 00000000000..f656cdfd97a --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main(&[1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/generics.md new file mode 100644 index 00000000000..0c1c27a2221 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/globals.md new file mode 100644 index 00000000000..063a3d89248 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/globals.md @@ -0,0 +1,72 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type can be inferred by the compiler entirely: + +```rust +global N = 5; // Same as `global N: Field = 5` + +global TUPLE = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: Field = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/mutability.md new file mode 100644 index 00000000000..fdeef6a87c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/ops.md new file mode 100644 index 00000000000..c35c36c38a9 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer, shift must be u8 | +| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/oracles.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/oracles.md new file mode 100644 index 00000000000..aa380b5f7b8 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/oracles.md @@ -0,0 +1,31 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +:::note + +This is an experimental feature that is not fully documented. If you notice any outdated information or potential improvements to this page, pull request contributions are very welcome: https://github.com/noir-lang/noir + +::: + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` + +The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/traits.md new file mode 100644 index 00000000000..df7cb9ebda0 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.31.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.31.0/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..96f824c5e42 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/concepts/unconstrained.md @@ -0,0 +1,99 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..95ee9f52ab2 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..24e02de08fe --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use ecrecover; +use lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use std::hash::sha256; +use std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..513497f12bf --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,42 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│ ├── a +│ │ ├── Nargo.toml +│ │ └── Prover.toml +│ │ └── src +│ │ └── main.nr +│ └── b +│ ├── Nargo.toml +│ └── Prover.toml +│ └── src +│ └── main.nr +│ +└── Nargo.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.31.0/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/bigint.md new file mode 100644 index 00000000000..2bfdeec6631 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/bigint.md @@ -0,0 +1,122 @@ +--- +title: Big Integers +description: How to use big integers from Noir standard library +keywords: + [ + Big Integer, + Noir programming language, + Noir libraries, + ] +--- + +The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. + +:::note + +The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. + +::: + +Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: + +- BN254 Fq: Bn254Fq +- BN254 Fr: Bn254Fr +- Secp256k1 Fq: Secpk1Fq +- Secp256k1 Fr: Secpk1Fr +- Secp256r1 Fr: Secpr1Fr +- Secp256r1 Fq: Secpr1Fq + +Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. +For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. + +Feel free to explore the source code for the other primes: + +```rust title="big_int_definition" showLineNumbers +struct BigInt { + pointer: u32, + modulus: u32, +} +``` +> Source code: noir_stdlib/src/bigint.nr#L14-L19 + + +## Example usage + +A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: + +```rust title="big_int_example" showLineNumbers +fn big_int_example(x: u8, y: u8) { + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); + let c = (a + b) * b / a; + let d = c.to_le_bytes(); + println(d[0]); +} +``` +> Source code: test_programs/execution_success/bigint/src/main.nr#L70-L78 + + +## Methods + +The available operations for each big integer are: + +### from_le_bytes + +Construct a big integer from its little-endian bytes representation. Example: + +```rust + // Construct a big integer from a slice of bytes + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + // Construct a big integer from an array of 32 bytes + let a = Secpk1Fq::from_le_bytes_32([1;32]); + ``` + +Sure, here's the formatted version of the remaining methods: + +### to_le_bytes + +Return the little-endian bytes representation of a big integer. Example: + +```rust +let bytes = a.to_le_bytes(); +``` + +### add + +Add two big integers. Example: + +```rust +let sum = a + b; +``` + +### sub + +Subtract two big integers. Example: + +```rust +let difference = a - b; +``` + +### mul + +Multiply two big integers. Example: + +```rust +let product = a * b; +``` + +### div + +Divide two big integers. Note that division is field division and not euclidean division. Example: + +```rust +let quotient = a / b; +``` + +### eq + +Compare two big integers. Example: + +```rust +let are_equal = a == b; +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..d5694250f05 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/black_box_fns.md @@ -0,0 +1,32 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.md) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/bn254.md new file mode 100644 index 00000000000..3294f005dbb --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/boundedvec.md new file mode 100644 index 00000000000..4349eab67c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,419 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn foo() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of foo's return value + v2 +} + +fn bad() { + let mut v3 = BoundedVec::new(); + + // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + assert(first != 42); + v.set(0, 42); + let new_first = v.get(0); + assert(new_first == 42); +} +``` + +### set_unchecked + +```rust +pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { +``` + +Writes an element to the vector at the given index, starting from zero, without performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! + +Example: + +```rust title="set_unchecked_example" showLineNumbers +fn set_unchecked_example() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2]); + + // Here we're safely writing within the valid range of `vec` + // `vec` now has the value [42, 2] + vec.set_unchecked(0, 42); + + // We can then safely read this value back out of `vec`. + // Notice that we use the checked version of `get` which would prevent reading unsafe values. + assert_eq(vec.get(0), 42); + + // We've now written past the end of `vec`. + // As this index is still within the maximum potential length of `v`, + // it won't cause a constraint failure. + vec.set_unchecked(2, 42); + println(vec); + + // This will write past the end of the maximum potential length of `vec`, + // it will then trigger a constraint failure. + vec.set_unchecked(5, 42); + println(vec); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 + + +### from_array + +```rust +pub fn from_array(array: [T; Len]) -> Self +``` + +Creates a new vector, populating it with values derived from an array input. +The maximum length of the vector is determined based on the type signature. + +Example: +```rust +let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) +``` + +### map + +```rust +pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec +``` + +Creates a new vector of equal size by calling a closure on each element in this vector. + +Example: + +```rust title="bounded-vec-map-example" showLineNumbers +let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + let result = vec.map(|value| value * 2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L214-L217 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/hashmap.md new file mode 100644 index 00000000000..408b6ba9da7 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/hashmap.md @@ -0,0 +1,570 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already +known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which +will likely change the result of the program. This behavior is set to become an error in future +versions instead. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a poseidon2 hasher +use std::hash::poseidon2::Poseidon2Hasher; +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L196-L198 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L82-L86 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L211 + + +### get + +```rust title="get" showLineNumbers +pub fn get( + self, + key: K + ) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L278-L287 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L299-L307 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert( + &mut self, + key: K, + value: V + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L313-L323 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L213-L217 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove( + &mut self, + key: K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L356-L365 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L221-L228 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L115-L117 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L230-L238 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L264-L266 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L240-L255 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L271-L273 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L261 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L93-L95 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key( + self, + key: K + ) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L101-L110 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L310-L321 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L144-L146 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L323-L333 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L164-L166 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L335-L344 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut( + &mut self, + f: fn(K, V) -> (K, V) + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L183-L192 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut( + &mut self, + f: fn(K) -> K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L208-L217 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L353-L356 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L233-L235 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L358-L361 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L247-L249 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L281-L283 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher +{ + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L426-L435 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L285-L296 + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/index.md new file mode 100644 index 00000000000..ea84c6d5c21 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/vec.mdx new file mode 100644 index 00000000000..475011922f8 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/containers/vec.mdx @@ -0,0 +1,170 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +Panics if the index points beyond the vector's end. + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +vector.set(1, 42); +assert(vector.get(1) == 42); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ciphers.mdx new file mode 100644 index 00000000000..e83e26efb97 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -0,0 +1,32 @@ +--- +title: Ciphers +description: + Learn about the implemented ciphers ready to use for any Noir project +keywords: + [ciphers, Noir project, aes128, encrypt] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## aes128 + +Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. + +```rust title="aes128" showLineNumbers +pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} +``` +> Source code: noir_stdlib/src/aes128.nr#L2-L4 + + +```rust +fn main() { + let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. + let iv: [u8; 16] = [0; 16]; // Initialisation vector + let key: [u8; 16] = [0; 16] // AES key + let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. +} +``` + + + \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..f839b4a228e --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..4394b48f907 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,98 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +```rust title="ecdsa_secp256k1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 + + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +```rust title="ecdsa_secp256r1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 + + + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..1ad42a5ac96 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,37 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + +It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. +For instance, if you want to use Poseidon2 instead, you can do the following: +```rust +use std::hash::poseidon2::Poseidon2Hasher; + +eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); +``` + + + +## eddsa::eddsa_to_pub + +Private to public key conversion. + +Returns `(pub_key_x, pub_key_y)` + +```rust +fn eddsa_to_pub(secret : Field) -> (Field, Field) +``` + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx new file mode 100644 index 00000000000..14d7dda7f0d --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -0,0 +1,98 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplication in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. +For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +:::note +Suffixes `_low` and `_high` denote low and high limbs of a scalar. +::: + +## embedded_curve_ops::multi_scalar_mul + +Performs multi scalar multiplication over the embedded curve. +The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over +the curve and returns a sum of the resulting points. + +Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. + +```rust title="multi_scalar_mul" showLineNumbers +pub fn multi_scalar_mul( + points: [EmbeddedCurvePoint; N], + scalars: [EmbeddedCurveScalar; N] +) -> [Field; 3] +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L62-L67 + + +example + +```rust +fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); + println(point); +} +``` + +## embedded_curve_ops::fixed_base_scalar_mul + +Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). +The function accepts a single scalar on the input represented as 2 fields. + +```rust title="fixed_base_scalar_mul" showLineNumbers +pub fn fixed_base_scalar_mul( + scalar_low: Field, + scalar_high: Field +) -> [Field; 3] +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L70-L75 + + +example + +```rust +fn main(scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); + println(point); +} +``` + +## embedded_curve_ops::embedded_curve_add + +Adds two points on the embedded curve. +This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. + +### Parameters: +- `point1` (`EmbeddedCurvePoint`): The first point to add. +- `point2` (`EmbeddedCurvePoint`): The second point to add. + +### Returns: +- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. + +```rust title="embedded_curve_add" showLineNumbers +fn embedded_curve_add( + point1: EmbeddedCurvePoint, + point2: EmbeddedCurvePoint +) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L84-L89 + + +example + +```rust +fn main() { + let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; + let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; + let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); + println!("Resulting Point: ({}, {})", result.x, result.y); +} +``` + + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..18132b9a4dc --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,246 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. +Specify a message_size to hash only the first `message_size` bytes of the input. + +```rust title="sha256" showLineNumbers +pub fn sha256(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L11-L13 + + +example: +```rust title="sha256_var" showLineNumbers +let digest = std::hash::sha256_var([x as u8], 1); +``` +> Source code: test_programs/execution_success/sha256/src/main.nr#L16-L18 + + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::sha256::sha256_var(x, 4); +} +``` + + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L17-L19 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L23-L25 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L42-L44 + + +example: + +```rust title="pedersen-hash" showLineNumbers +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/hash.nr#L28-L30 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L64-L66 + + +example: + +```rust title="keccak256" showLineNumbers +fn main(x: Field, result: [u8; 32]) { + // 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 = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use std::hash::poseidon; +use std::hash::poseidon2; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field; 4], y3: Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); + + let hash3 = poseidon2::Poseidon2::hash(x3, x3.len()); + assert(hash3 == y3); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L15 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +The above example for Poseidon also includes Poseidon2. + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..b59e69c8f07 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,64 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). +See schnorr::verify_signature_slice for a version that works directly on slices. + +```rust title="schnorr_verify" showLineNumbers +pub fn verify_signature( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L2-L9 + + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + + +## schnorr::verify_signature_slice + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) +where the message is a slice. + +```rust title="schnorr_verify_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L13-L20 + + + diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/is_unconstrained.md new file mode 100644 index 00000000000..bb157e719dc --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/is_unconstrained.md @@ -0,0 +1,59 @@ +--- +title: Is Unconstrained Function +description: + The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. +keywords: + [ + unconstrained + ] +--- + +It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. + +When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: + +```rust + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + my_expensive_computation_hint(); + // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant + ... +} + +``` + +In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: + + +```rust +use dep::std::runtime::is_unconstrained; + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + if is_unconstrained() { + my_expensive_computation(); + } else { + my_expensive_computation_hint(); + // verify my_expensive_computation + ... + } +} + +``` + +The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..6a9ebf72ada --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/options.md new file mode 100644 index 00000000000..a1bd4e1de5f --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/recursion.md new file mode 100644 index 00000000000..8cfb37fc52d --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/recursion.md @@ -0,0 +1,85 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## The `#[recursive]` Attribute + +In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. + +### Example usage with `#[recursive]` + +```rust +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); +} + +// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit +// are intended for recursive verification. +``` + +By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. + +## Verifying Recursive Proofs + +```rust +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +``` + + + +## Example usage + +```rust + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/traits.md new file mode 100644 index 00000000000..a14312d2792 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/traits.md @@ -0,0 +1,464 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L1-L5 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for [T] { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. + + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +``` +> Source code: noir_stdlib/src/convert.nr#L25-L52 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +trait Into { + fn into(self) -> T; +} + +impl Into for U where T: From { + fn into(self) -> T { + T::from(self) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L23 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L1-L5 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for [T] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L102-L106 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for [T] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L19-L23 + +```rust title="mul-trait" showLineNumbers +trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L37-L41 + +```rust title="div-trait" showLineNumbers +trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L55-L59 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +trait Rem{ + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L73-L77 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::Neg` + +```rust title="neg-trait" showLineNumbers +trait Neg { + fn neg(self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L89-L93 + + +`Neg::neg` is equivalent to the unary negation operator `-`. + +Implementations: +```rust title="neg-trait-impls" showLineNumbers +impl Neg for Field { fn neg(self) -> Field { -self } } + +impl Neg for i8 { fn neg(self) -> i8 { -self } } +impl Neg for i16 { fn neg(self) -> i16 { -self } } +impl Neg for i32 { fn neg(self) -> i32 { -self } } +impl Neg for i64 { fn neg(self) -> i64 { -self } } +``` +> Source code: noir_stdlib/src/ops/arith.nr#L95-L102 + + +### `std::ops::Not` + +```rust title="not-trait" showLineNumbers +trait Not { + fn not(self: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 + + +`Not::not` is equivalent to the unary bitwise NOT operator `!`. + +Implementations: +```rust title="not-trait-impls" showLineNumbers +impl Not for bool { fn not(self) -> bool { !self } } + +impl Not for u64 { fn not(self) -> u64 { !self } } +impl Not for u32 { fn not(self) -> u32 { !self } } +impl Not for u16 { fn not(self) -> u16 { !self } } +impl Not for u8 { fn not(self) -> u8 { !self } } +impl Not for u1 { fn not(self) -> u1 { !self } } + +impl Not for i8 { fn not(self) -> i8 { !self } } +impl Not for i16 { fn not(self) -> i16 { !self } } +impl Not for i32 { fn not(self) -> i32 { !self } } +impl Not for i64 { fn not(self) -> i64 { !self } } +``` +> Source code: noir_stdlib/src/ops/bit.nr#L7-L20 + + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L22-L26 + +```rust title="bitand-trait" showLineNumbers +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L40-L44 + +```rust title="bitxor-trait" showLineNumbers +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L58-L62 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +trait Shl { + fn shl(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L76-L80 + +```rust title="shr-trait" showLineNumbers +trait Shr { + fn shr(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L93-L97 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/docs/versioned_docs/version-v0.31.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.31.0/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..f450fecdd36 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/noir/standard_library/zeroed.md @@ -0,0 +1,26 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..d7249d24330 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,160 @@ +# BarretenbergBackend + +## Extends + +- `BarretenbergVerifierBackend` + +## Implements + +- [`Backend`](../index.md#backend) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | `CompiledCircuit` | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +#### Inherited from + +BarretenbergVerifierBackend.constructor + +## Properties + +| Property | Type | Description | Inheritance | +| :------ | :------ | :------ | :------ | +| `acirComposer` | `any` | - | BarretenbergVerifierBackend.acirComposer | +| `acirUncompressedBytecode` | `Uint8Array` | - | BarretenbergVerifierBackend.acirUncompressedBytecode | +| `api` | `Barretenberg` | - | BarretenbergVerifierBackend.api | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - | BarretenbergVerifierBackend.options | + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Inherited from + +BarretenbergVerifierBackend.destroy + +*** + +### generateProof() + +```ts +generateProof(compressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `compressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<`ProofData`\> + +#### Description + +Generates a proof + +*** + +### generateRecursiveProofArtifacts() + +```ts +generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +Generates artifacts that will be passed to a circuit that will verify this proof. + +Instead of passing the proof and verification key as a byte array, we pass them +as fields which makes it cheaper to verify in a circuit. + +The proof that is passed here will have been created using a circuit +that has the #[recursive] attribute on its `main` method. + +The number of public inputs denotes how many public inputs are in the inner proof. + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | `ProofData` | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Example + +```typescript +const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### getVerificationKey() + +```ts +getVerificationKey(): Promise +``` + +#### Returns + +`Promise`\<`Uint8Array`\> + +#### Inherited from + +BarretenbergVerifierBackend.getVerificationKey + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | `ProofData` | + +#### Returns + +`Promise`\<`boolean`\> + +#### Inherited from + +BarretenbergVerifierBackend.verifyProof + +#### Description + +Verifies a proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md new file mode 100644 index 00000000000..500276ea748 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md @@ -0,0 +1,58 @@ +# BarretenbergVerifier + +## Constructors + +### new BarretenbergVerifier(options) + +```ts +new BarretenbergVerifier(options): BarretenbergVerifier +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergVerifier`](BarretenbergVerifier.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +*** + +### verifyProof() + +```ts +verifyProof(proofData, verificationKey): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | `ProofData` | +| `verificationKey` | `Uint8Array` | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/index.md new file mode 100644 index 00000000000..14dfac681d4 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/index.md @@ -0,0 +1,40 @@ +# backend_barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | +| [BarretenbergVerifier](classes/BarretenbergVerifier.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | + +## References + +### CompiledCircuit + +Renames and re-exports [Backend](index.md#backend) + +*** + +### ProofData + +Renames and re-exports [Backend](index.md#backend) + +## Variables + +### Backend + +```ts +Backend: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..b49a479f4f4 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,21 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `memory` | `object` | - | +| `memory.maximum` | `number` | - | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..d7d5128f9e3 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier","label":"BarretenbergVerifier"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 00000000000..ead255bc504 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,52 @@ +# Noir + +## Constructors + +### new Noir(circuit) + +```ts +new Noir(circuit): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | `CompiledCircuit` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..5e3cd53e9d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/index.md new file mode 100644 index 00000000000..166508f7124 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/index.md @@ -0,0 +1,49 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [ErrorWithPayload](type-aliases/ErrorWithPayload.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +## References + +### CompiledCircuit + +Renames and re-exports [InputMap](index.md#inputmap) + +## Variables + +### InputMap + +```ts +InputMap: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md new file mode 100644 index 00000000000..e8c2f4aef3d --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md @@ -0,0 +1,15 @@ +# ErrorWithPayload + +```ts +type ErrorWithPayload: ExecutionError & object; +``` + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `decodedAssertionPayload` | `any` | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..b3156097df6 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 00000000000..6faf763b37f --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 00000000000..7d0b39a43ef --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 00000000000..7e65c1d69c7 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 00000000000..fcea9275341 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 00000000000..b6e0f9d1bc0 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 00000000000..e0870710349 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/_category_.json b/docs/versioned_docs/version-v0.31.0/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.31.0/reference/debugger/_category_.json new file mode 100644 index 00000000000..27869205ad3 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugger", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_known_limitations.md new file mode 100644 index 00000000000..936d416ac4b --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_known_limitations.md @@ -0,0 +1,59 @@ +--- +title: Known limitations +description: + An overview of known limitations of the current version of the Noir debugger +keywords: + [ + Nargo, + Noir Debugger, + VS Code, + ] +sidebar_position: 2 +--- + +# Debugger Known Limitations + +There are currently some limits to what the debugger can observe. + +## Mutable references + +The debugger is currently blind to any state mutated via a mutable reference. For example, in: + +``` +let mut x = 1; +let y = &mut x; +*y = 2; +``` + +The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. + +## Variables of type function or mutable references are opaque + +When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. + +## Debugger instrumentation affects resulting ACIR + +In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: + +``` +... +5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] + | outputs=[] + 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } + 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } + 5.6 | Call { location: 8 } + 5.7 | Stop + 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } +... +``` + +If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). + +:::note +Skipping debugger instrumentation means you won't be able to inspect values of local variables. +::: + diff --git a/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_repl.md new file mode 100644 index 00000000000..46e2011304e --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_repl.md @@ -0,0 +1,360 @@ +--- +title: REPL Debugger +description: + Noir Debugger REPL options and commands. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +## Running the REPL debugger + +`nargo debug [OPTIONS] [WITNESS_NAME]` + +Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` file. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover]| +| `--package ` | The name of the package to debug | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +None of these options are required. + +:::note +Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. +::: + +## REPL commands + +Once the debugger is running, it accepts the following commands. + +#### `help` (h) + +Displays the menu of available commands. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) value + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +### Stepping through programs + +#### `next` (n) + +Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). + +If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. + +#### `over` + +Step until the next source code location, without diving into function calls. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). + +If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). + +#### `out` + +Step until the end of the current function call. For example: + +``` + 3 ... + 4 fn main(x: u32) { + 5 assert(entry_point(x) == 2); + 6 swap_entry_point(x, x + 1); + 7 -> assert(deep_entry_point(x) == 4); + 8 multiple_values_entry_point(x); + 9 } + 10 + 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { + 12 ... + ... + 55 + 56 unconstrained fn deep_entry_point(x: u32) -> u32 { + 57 -> level_1(x + 1) + 58 } + +``` + +Running `out` here will resume execution until line 8. + +#### `step` (s) + +Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. + +Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. + +#### `into` (i) + +Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. + +Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. + +#### `continue` (c) + +Continues execution until the next breakpoint, or the end of the program. + +#### `restart` (res) + +Interrupts execution, and restarts a new debugging session from scratch. + +#### `opcodes` (o) + +Display the program's ACIR opcode sequence. For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +### Breakpoints + +#### `break [Opcode]` (or shorthand `b [Opcode]`) + +Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. + +#### `delete [Opcode]` (or shorthand `d [Opcode]`) + +Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). + +### Variable inspection + +#### vars + +Show variable values available at this point in execution. + +:::note +The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. + +So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. + +If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. +::: + + +### Stacktrace + +#### `stacktrace` + +Displays the current stack trace. + + +### Witness map + +#### `witness` (w) + +Show witness map. For example: + +``` +_0 = 0 +_1 = 2 +_2 = 1 +``` + +#### `witness [Witness Index]` + +Display a single witness from the witness map. For example: + +``` +> witness 1 +_1 = 2 +``` + +#### `witness [Witness Index] [New value]` + +Overwrite the given index with a new value. For example: + +``` +> witness 1 3 +_1 = 3 +``` + + +### Unconstrained VM memory + +#### `memory` + +Show unconstrained VM memory state. For example: + +``` +> memory +At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } +... +> registers +0 = 0 +1 = 10 +2 = 0 +3 = 1 +4 = 1 +5 = 2³² +6 = 1 +> into +At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } +... +> memory +0 = 1 +> +``` + +In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: + +#### `memset [Memory address] [New value]` + +Update a memory cell with the given value. For example: + +``` +> memory +0 = 1 +> memset 0 2 +> memory +0 = 2 +> memset 1 4 +> memory +0 = 2 +1 = 4 +> +``` + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_vscode.md new file mode 100644 index 00000000000..c027332b3b0 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/debugger/debugger_vscode.md @@ -0,0 +1,82 @@ +--- +title: VS Code Debugger +description: + VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +# VS Code Noir Debugger Reference + +The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. + +These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. + + +## Creating and editing launch configuration files + +To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. + +![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) + +A `launch.json` file will be created, populated with basic defaults. + +### Noir Debugger launch.json properties + +#### projectFolder + +_String, optional._ + +Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. + +#### proverName + +_String, optional._ + +Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. + +#### generateAcir + +_Boolean, optional._ + +If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. + +#### skipInstrumentation + +_Boolean, optional._ + +Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. + +:::note +Skipping instrumentation causes the debugger to be unable to inspect local variables. +::: + +## `nargo dap [OPTIONS]` + +When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. + +All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. + +Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. + +`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. + +If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. + +### Options + +| Option | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| `--preflight-check` | If present, dap runs in preflight check mode. | +| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | +| `--preflight-prover-name ` | Name of prover file to use for preflight check | +| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | +| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | +| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.31.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.31.0/reference/nargo_commands.md new file mode 100644 index 00000000000..ab9dd879d85 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/reference/nargo_commands.md @@ -0,0 +1,244 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `check` — Checks the constraint system for errors +* `fmt` — Format the Noir files in a workspace +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `test` — Run the tests for this program +* `info` — Provides detailed information on each of a program's function (represented by a single circuit) +* `lsp` — Starts the Noir LSP server + +###### **Options:** + + + + +## `nargo check` + +Checks the constraint system for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to check +* `--workspace` — Check all packages in the workspace +* `--overwrite` — Force overwrite of existing files +* `--expression-width ` — Override the expression width requested by the backend + + Default value: `4` +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to compile +* `--workspace` — Compile all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend + + Default value: `4` +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--workspace` — Execute all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend + + Default value: `4` +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAME]` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements +* `--exact` — Only run tests that match exactly +* `--package ` — The name of the package to test +* `--workspace` — Test all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend + + Default value: `4` +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo info` + +Provides detailed information on each of a program's function (represented by a single circuit) + +Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to detail +* `--workspace` — Detail all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend + + Default value: `4` +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/docs/versioned_docs/version-v0.31.0/tooling/debugger.md b/docs/versioned_docs/version-v0.31.0/tooling/debugger.md new file mode 100644 index 00000000000..9b7565ba9ff --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/tooling/debugger.md @@ -0,0 +1,26 @@ +--- +title: Debugger +description: Learn about the Noir Debugger, in its REPL or VS Code versions. +keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] +sidebar_position: 2 +--- + +# Noir Debugger + +There are currently two ways of debugging Noir programs: + +1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). +2. Via the REPL debugger, which ships with Nargo. + +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: + +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 + +:::info +At the moment, the debugger supports debugging binary projects, but not contracts. +::: + +We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). + +The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.31.0/tooling/language_server.md b/docs/versioned_docs/version-v0.31.0/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/docs/versioned_docs/version-v0.31.0/tooling/testing.md b/docs/versioned_docs/version-v0.31.0/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md new file mode 100644 index 00000000000..cbb1938a5c6 --- /dev/null +++ b/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md @@ -0,0 +1,327 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.27.x matches `noir_js@0.27.x`, etc. + +In this guide, we will be pinned to 0.27.0. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +`nargo new circuit` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +`nargo compile` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: + +```bash +cd vite-project +``` + +### Setting Up Vite and Configuring the Project + +Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: + +#### Creating the vite.config.js + +In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: + +```javascript +import { defineConfig } from "vite"; +import copy from "rollup-plugin-copy"; + +export default defineConfig({ + esbuild: { + target: "esnext", + }, + optimizeDeps: { + esbuildOptions: { + target: "esnext", + }, + }, + plugins: [ + copy({ + targets: [ + { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, + ], + copySync: true, + hook: "buildStart", + }), + ], + server: { + port: 3000, + }, +}); +``` + +#### Install Dependencies + +Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: + +```bash +npm install && npm install @noir-lang/backend_barretenberg@0.27.0 @noir-lang/noir_js@0.27.0 +npm install rollup-plugin-copy --save-dev +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It _could_ be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import('@noir-lang/noirc_abi').then((module) => + module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), + ), + import('@noir-lang/acvm_js').then((module) => + module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), + ), + ]); +}; + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch (err) { + display('logs', 'Oh 💔 Wrong guess'); + } +}); +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── vite.config.js + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const { witness } = await noir.execute(input); +const proof = await backend.generateProof(witness); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verificationKey = await backend.getVerificationKey(); +const verifier = new Verifier(); +const isValid = await verifier.verifyProof(proof, verificationKey); +if (isValid) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/docs/versioned_sidebars/version-v0.31.0-sidebars.json b/docs/versioned_sidebars/version-v0.31.0-sidebars.json new file mode 100644 index 00000000000..b9ad026f69f --- /dev/null +++ b/docs/versioned_sidebars/version-v0.31.0-sidebars.json @@ -0,0 +1,93 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "category", + "label": "Tooling", + "items": [ + { + "type": "autogenerated", + "dirName": "tooling" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json index 5d3a7d6315e..eb9694a3b0c 100644 --- a/tooling/noir_codegen/package.json +++ b/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.30.0", + "version": "0.31.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index eca3f29957f..3bb2ab5826f 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.30.0", + "version": "0.31.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index af3011068bf..2bb142c9fa0 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.30.0", + "version": "0.31.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index b2b84b640a5..0de1a1fd4be 100644 --- a/tooling/noir_js_types/package.json +++ b/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "0.30.0", + "version": "0.31.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index 399a333f157..b33bb159fcd 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.30.0", + "version": "0.31.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { From 55d8e05b84ae23a1a218e7027542af175979885c Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 17 Jun 2024 15:08:25 -0500 Subject: [PATCH 38/57] chore: Use the elaborator by default (#5246) # Description ## Problem\* ## Summary\* Removes the `--use-elaborator` flag, replacing it with `--use-legacy` to enable the old name resolver & type checker passes which are now off by default in favor of the elaborator. ## Additional Context No user visible changes are expected from this PR. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_driver/src/lib.rs | 12 ++-- compiler/noirc_frontend/src/elaborator/mod.rs | 2 + .../src/hir/def_collector/dc_crate.rs | 6 +- .../noirc_frontend/src/hir/def_map/mod.rs | 4 +- tooling/nargo_cli/build.rs | 55 +++++++++---------- tooling/nargo_cli/src/cli/check_cmd.rs | 6 +- tooling/nargo_cli/src/cli/export_cmd.rs | 2 +- tooling/nargo_cli/src/cli/test_cmd.rs | 4 +- 8 files changed, 46 insertions(+), 45 deletions(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 2c8679eaa95..dde2bd08d74 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -99,9 +99,9 @@ pub struct CompileOptions { #[arg(long, hide = true)] pub force_brillig: bool, - /// Enable the experimental elaborator pass + /// Use the deprecated name resolution & type checking passes instead of the elaborator #[arg(long, hide = true)] - pub use_elaborator: bool, + pub use_legacy: bool, /// Outputs the paths to any modified artifacts #[arg(long, hide = true)] @@ -257,13 +257,13 @@ pub fn check_crate( crate_id: CrateId, deny_warnings: bool, disable_macros: bool, - use_elaborator: bool, + use_legacy: bool, ) -> CompilationResult<()> { let macros: &[&dyn MacroProcessor] = if disable_macros { &[] } else { &[&aztec_macros::AztecMacro as &dyn MacroProcessor] }; let mut errors = vec![]; - let diagnostics = CrateDefMap::collect_defs(crate_id, context, use_elaborator, macros); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, use_legacy, macros); errors.extend(diagnostics.into_iter().map(|(error, file_id)| { let diagnostic = CustomDiagnostic::from(&error); diagnostic.in_file(file_id) @@ -300,7 +300,7 @@ pub fn compile_main( crate_id, options.deny_warnings, options.disable_macros, - options.use_elaborator, + options.use_legacy, )?; let main = context.get_main_function(&crate_id).ok_or_else(|| { @@ -341,7 +341,7 @@ pub fn compile_contract( crate_id, options.deny_warnings, options.disable_macros, - options.use_elaborator, + options.use_legacy, )?; // TODO: We probably want to error if contracts is empty diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 93e8b36f4e0..0b7e5a58849 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1138,6 +1138,7 @@ impl<'context> Elaborator<'context> { fn elaborate_global(&mut self, global: UnresolvedGlobal) { let old_module = std::mem::replace(&mut self.local_module, global.module_id); let old_file = std::mem::replace(&mut self.file, global.file_id); + let old_item = self.current_item.take(); let global_id = global.global_id; self.current_item = Some(DependencyId::Global(global_id)); @@ -1171,6 +1172,7 @@ impl<'context> Elaborator<'context> { self.type_variables.clear(); self.local_module = old_module; self.file = old_file; + self.current_item = old_item; } fn elaborate_comptime_global(&mut self, global_id: GlobalId) { diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 4d64481bc9f..216ed5fc545 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -256,7 +256,7 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, - use_elaborator: bool, + use_legacy: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; @@ -273,7 +273,7 @@ impl DefCollector { errors.extend(CrateDefMap::collect_defs( dep.crate_id, context, - use_elaborator, + use_legacy, macro_processors, )); @@ -351,7 +351,7 @@ impl DefCollector { } } - if use_elaborator { + if !use_legacy { let mut more_errors = Elaborator::elaborate(context, crate_id, def_collector.items); errors.append(&mut more_errors); return errors; diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 19e06387d43..59205f74d89 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -73,7 +73,7 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, - use_elaborator: bool, + use_legacy: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled @@ -122,7 +122,7 @@ impl CrateDefMap { context, ast, root_file_id, - use_elaborator, + use_legacy, macro_processors, )); diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 846160320cf..d2822ba11ab 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -87,23 +87,23 @@ fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) test_file, r#" #[test] -fn execution_success_{test_name}() {{ +fn execution_success_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force"); + cmd.arg("execute").arg("--force").arg("--use-legacy"); cmd.assert().success(); }} #[test] -fn execution_success_elaborator_{test_name}() {{ +fn execution_success_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force").arg("--use-elaborator"); + cmd.arg("execute").arg("--force"); cmd.assert().success(); }} @@ -146,23 +146,23 @@ fn generate_execution_failure_tests(test_file: &mut File, test_data_dir: &Path) test_file, r#" #[test] -fn execution_failure_{test_name}() {{ +fn execution_failure_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force"); + cmd.arg("execute").arg("--force").arg("--use-legacy"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); }} #[test] -fn execution_failure_elaborator_{test_name}() {{ +fn execution_failure_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force").arg("--use-elaborator"); + cmd.arg("execute").arg("--force"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); }} @@ -194,23 +194,23 @@ fn generate_noir_test_success_tests(test_file: &mut File, test_data_dir: &Path) test_file, r#" #[test] -fn noir_test_success_{test_name}() {{ +fn noir_test_success_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test"); + cmd.arg("test").arg("--use-legacy"); cmd.assert().success(); }} #[test] -fn noir_test_success_elaborator_{test_name}() {{ +fn noir_test_success_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test").arg("--use-elaborator"); + cmd.arg("test"); cmd.assert().success(); }} @@ -242,23 +242,23 @@ fn generate_noir_test_failure_tests(test_file: &mut File, test_data_dir: &Path) test_file, r#" #[test] -fn noir_test_failure_{test_name}() {{ +fn noir_test_failure_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test"); + cmd.arg("test").arg("--use-legacy"); cmd.assert().failure(); }} #[test] -fn noir_test_failure_elaborator_{test_name}() {{ +fn noir_test_failure_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test").arg("--use-elaborator"); + cmd.arg("test"); cmd.assert().failure(); }} @@ -293,14 +293,14 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa test_file, r#" #[test]{comptime_ignored} -fn compile_success_empty_{test_name}() {{ - +fn compile_success_empty_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); cmd.arg("info"); cmd.arg("--json"); cmd.arg("--force"); + cmd.arg("--use-legacy"); let output = cmd.output().expect("Failed to execute command"); @@ -317,14 +317,13 @@ fn compile_success_empty_{test_name}() {{ }} #[test] -fn compile_success_empty_elaborator_{test_name}() {{ +fn compile_success_empty_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); cmd.arg("info"); cmd.arg("--json"); cmd.arg("--force"); - cmd.arg("--use-elaborator"); let output = cmd.output().expect("Failed to execute command"); @@ -367,22 +366,22 @@ fn generate_compile_success_contract_tests(test_file: &mut File, test_data_dir: test_file, r#" #[test] -fn compile_success_contract_{test_name}() {{ +fn compile_success_contract_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force"); + cmd.arg("compile").arg("--force").arg("--use-legacy"); cmd.assert().success(); }} #[test] -fn compile_success_contract_elaborator_{test_name}() {{ +fn compile_success_contract_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force").arg("--use-elaborator"); + cmd.arg("compile").arg("--force"); cmd.assert().success(); }} @@ -414,22 +413,22 @@ fn generate_compile_failure_tests(test_file: &mut File, test_data_dir: &Path) { test_file, r#" #[test] -fn compile_failure_{test_name}() {{ +fn compile_failure_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force"); + cmd.arg("compile").arg("--force").arg("--use-legacy"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); }} #[test] -fn compile_failure_elaborator_{test_name}() {{ +fn compile_failure_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force").arg("--use-elaborator"); + cmd.arg("compile").arg("--force"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); }} diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index e2e1f147b90..2db3b10429a 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -87,7 +87,7 @@ fn check_package( compile_options.deny_warnings, compile_options.disable_macros, compile_options.silence_warnings, - compile_options.use_elaborator, + compile_options.use_legacy, )?; if package.is_library() || package.is_contract() { @@ -160,9 +160,9 @@ pub(crate) fn check_crate_and_report_errors( deny_warnings: bool, disable_macros: bool, silence_warnings: bool, - use_elaborator: bool, + use_legacy: bool, ) -> Result<(), CompileError> { - let result = check_crate(context, crate_id, deny_warnings, disable_macros, use_elaborator); + let result = check_crate(context, crate_id, deny_warnings, disable_macros, use_legacy); report_errors(result, &context.file_manager, deny_warnings, silence_warnings) } diff --git a/tooling/nargo_cli/src/cli/export_cmd.rs b/tooling/nargo_cli/src/cli/export_cmd.rs index 324eed340ad..ee30b29b0f0 100644 --- a/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/tooling/nargo_cli/src/cli/export_cmd.rs @@ -89,7 +89,7 @@ fn compile_exported_functions( compile_options.deny_warnings, compile_options.disable_macros, compile_options.silence_warnings, - compile_options.use_elaborator, + compile_options.use_legacy, )?; let exported_functions = context.get_all_exported_functions_in_crate(&crate_id); diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 99c284e5019..1cda8d8382e 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -175,7 +175,7 @@ fn run_test + Default>( crate_id, compile_options.deny_warnings, compile_options.disable_macros, - compile_options.use_elaborator, + compile_options.use_legacy, ) .expect("Any errors should have occurred when collecting test functions"); @@ -209,7 +209,7 @@ fn get_tests_in_package( compile_options.deny_warnings, compile_options.disable_macros, compile_options.silence_warnings, - compile_options.use_elaborator, + compile_options.use_legacy, )?; Ok(context From c91186a5c0d9e84767f160e6acd63672b23e8f52 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:36:35 +0100 Subject: [PATCH 39/57] feat: implement comptime support for `array_len` builtin (#5272) # Description ## Problem\* Resolves ## Summary\* This PR implements support for the `array_len` builtin so we can query the length of arrays in `comptime` blocks. Ideally we'd tie this closer to the `Intrinsics` struct in `noirc_evaluator` but we can handle this morn cleanly later. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_frontend/src/hir/comptime/interpreter.rs | 10 ++++++++-- .../src/hir/comptime/interpreter/builtin.rs | 12 ++++++++++++ .../comptime_array_len/Nargo.toml | 6 ++++++ .../comptime_array_len/src/main.nr | 6 ++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs create mode 100644 test_programs/compile_success_empty/comptime_array_len/Nargo.toml create mode 100644 test_programs/compile_success_empty/comptime_array_len/src/main.nr diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 3648881e501..c473ac7f8a6 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -31,6 +31,7 @@ use self::unquote::UnquoteArgs; use super::errors::{IResult, InterpreterError}; use super::value::Value; +mod builtin; mod unquote; #[allow(unused)] @@ -99,8 +100,13 @@ impl<'a> Interpreter<'a> { .expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); if let Some(builtin) = func_attrs.builtin() { - let item = format!("Evaluation for builtin functions like {builtin}"); - Err(InterpreterError::Unimplemented { item, location }) + match builtin.as_str() { + "array_len" => builtin::array_len(&arguments), + _ => { + let item = format!("Evaluation for builtin function {builtin}"); + Err(InterpreterError::Unimplemented { item, location }) + } + } } else if let Some(foreign) = func_attrs.foreign() { let item = format!("Evaluation for foreign functions like {foreign}"); Err(InterpreterError::Unimplemented { item, location }) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs new file mode 100644 index 00000000000..3e5ad207719 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -0,0 +1,12 @@ +use noirc_errors::Location; + +use crate::hir::comptime::{errors::IResult, Value}; + +pub(super) fn array_len(arguments: &[(Value, Location)]) -> IResult { + assert_eq!(arguments.len(), 1, "ICE: `array_len` should only receive a single argument"); + match &arguments[0].0 { + Value::Array(values, _) | Value::Slice(values, _) => Ok(Value::U32(values.len() as u32)), + // Type checking should prevent this branch being taken. + _ => unreachable!("ICE: Cannot query length of types other than arrays or slices"), + } +} diff --git a/test_programs/compile_success_empty/comptime_array_len/Nargo.toml b/test_programs/compile_success_empty/comptime_array_len/Nargo.toml new file mode 100644 index 00000000000..c07deddc6c5 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_array_len/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "comptime_array_len" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/comptime_array_len/src/main.nr b/test_programs/compile_success_empty/comptime_array_len/src/main.nr new file mode 100644 index 00000000000..c98a3de01dd --- /dev/null +++ b/test_programs/compile_success_empty/comptime_array_len/src/main.nr @@ -0,0 +1,6 @@ +fn main() { + comptime + { + assert_eq([1, 2, 3].len(), 3); + } +} From f8608e61957333707b8e651db42a00a7ed0928c3 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:55:29 +0100 Subject: [PATCH 40/57] chore: simplify compilation flow to write to file immediately (#5265) # Description ## Problem\* Resolves ## Summary\* This PR pulls across the changes from https://github.com/AztecProtocol/aztec-packages/pull/6488 to avoid making changes in aztec-packages unnecessarily and get around the CI issues over there. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/src/cli/compile_cmd.rs | 129 ++++++++++++++--------- 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index bd76cf24805..e83b1728c93 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -3,7 +3,6 @@ use std::path::Path; use std::time::Duration; use fm::FileManager; -use nargo::artifacts::program::ProgramArtifact; use nargo::ops::{collect_errors, compile_contract, compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; @@ -11,7 +10,7 @@ use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::file_manager_with_stdlib; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; -use noirc_driver::{CompilationResult, CompileOptions, CompiledContract, CompiledProgram}; +use noirc_driver::{CompilationResult, CompileOptions, CompiledContract}; use noirc_frontend::graph::CrateName; @@ -121,39 +120,22 @@ pub(super) fn compile_workspace_full( let compiled_workspace = compile_workspace(&workspace_file_manager, &parsed_files, workspace, compile_options); - let (compiled_programs, compiled_contracts) = report_errors( + report_errors( compiled_workspace, &workspace_file_manager, compile_options.deny_warnings, compile_options.silence_warnings, )?; - let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace - .into_iter() - .filter(|package| !package.is_library()) - .cloned() - .partition(|package| package.is_binary()); - - // Save build artifacts to disk. - for (package, program) in binary_packages.into_iter().zip(compiled_programs) { - let program = nargo::ops::transform_program(program, compile_options.expression_width); - save_program(program.clone(), &package, &workspace.target_directory_path()); - } - let circuit_dir = workspace.target_directory_path(); - for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { - let contract = nargo::ops::transform_contract(contract, compile_options.expression_width); - save_contract(contract, &package, &circuit_dir, compile_options.show_artifact_paths); - } - Ok(()) } -pub(super) fn compile_workspace( +fn compile_workspace( file_manager: &FileManager, parsed_files: &ParsedFiles, workspace: &Workspace, compile_options: &CompileOptions, -) -> CompilationResult<(Vec, Vec)> { +) -> CompilationResult<()> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace .into_iter() .filter(|package| !package.is_library()) @@ -161,32 +143,20 @@ pub(super) fn compile_workspace( .partition(|package| package.is_binary()); // Compile all of the packages in parallel. - let program_results: Vec> = binary_packages - .par_iter() - .map(|package| { - let program_artifact_path = workspace.package_build_path(package); - let cached_program: Option = - read_program_from_file(program_artifact_path) - .ok() - .filter(|p| p.noir_version == NOIR_ARTIFACT_VERSION_STRING) - .map(|p| p.into()); - - compile_program(file_manager, parsed_files, package, compile_options, cached_program) - }) - .collect(); - let contract_results: Vec> = contract_packages - .par_iter() - .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) - .collect(); - - // Collate any warnings/errors which were encountered during compilation. - let compiled_programs = collect_errors(program_results); - let compiled_contracts = collect_errors(contract_results); + let program_warnings_or_errors: CompilationResult<()> = + compile_programs(file_manager, parsed_files, workspace, &binary_packages, compile_options); + let contract_warnings_or_errors: CompilationResult<()> = compiled_contracts( + file_manager, + parsed_files, + &contract_packages, + compile_options, + &workspace.target_directory_path(), + ); - match (compiled_programs, compiled_contracts) { - (Ok((programs, program_warnings)), Ok((contracts, contract_warnings))) => { + match (program_warnings_or_errors, contract_warnings_or_errors) { + (Ok((_, program_warnings)), Ok((_, contract_warnings))) => { let warnings = [program_warnings, contract_warnings].concat(); - Ok(((programs, contracts), warnings)) + Ok(((), warnings)) } (Err(program_errors), Err(contract_errors)) => { Err([program_errors, contract_errors].concat()) @@ -195,22 +165,79 @@ pub(super) fn compile_workspace( } } -pub(super) fn save_program(program: CompiledProgram, package: &Package, circuit_dir: &Path) { - let program_artifact = ProgramArtifact::from(program.clone()); - save_program_to_file(&program_artifact, &package.name, circuit_dir); +fn compile_programs( + file_manager: &FileManager, + parsed_files: &ParsedFiles, + workspace: &Workspace, + binary_packages: &[Package], + compile_options: &CompileOptions, +) -> CompilationResult<()> { + let load_cached_program = |package| { + let program_artifact_path = workspace.package_build_path(package); + read_program_from_file(program_artifact_path) + .ok() + .filter(|p| p.noir_version == NOIR_ARTIFACT_VERSION_STRING) + .map(|p| p.into()) + }; + + let program_results: Vec> = binary_packages + .par_iter() + .map(|package| { + let (program, warnings) = compile_program( + file_manager, + parsed_files, + package, + compile_options, + load_cached_program(package), + )?; + let program = nargo::ops::transform_program(program, compile_options.expression_width); + save_program_to_file( + &program.clone().into(), + &package.name, + workspace.target_directory_path(), + ); + Ok(((), warnings)) + }) + .collect(); + + // Collate any warnings/errors which were encountered during compilation. + collect_errors(program_results).map(|(_, warnings)| ((), warnings)) +} + +fn compiled_contracts( + file_manager: &FileManager, + parsed_files: &ParsedFiles, + contract_packages: &[Package], + compile_options: &CompileOptions, + target_dir: &Path, +) -> CompilationResult<()> { + let contract_results: Vec> = contract_packages + .par_iter() + .map(|package| { + let (contract, warnings) = + compile_contract(file_manager, parsed_files, package, compile_options)?; + let contract = + nargo::ops::transform_contract(contract, compile_options.expression_width); + save_contract(contract, package, target_dir, compile_options.show_artifact_paths); + Ok(((), warnings)) + }) + .collect(); + + // Collate any warnings/errors which were encountered during compilation. + collect_errors(contract_results).map(|(_, warnings)| ((), warnings)) } fn save_contract( contract: CompiledContract, package: &Package, - circuit_dir: &Path, + target_dir: &Path, show_artifact_paths: bool, ) { let contract_name = contract.name.clone(); let artifact_path = save_contract_to_file( &contract.into(), &format!("{}-{}", package.name, contract_name), - circuit_dir, + target_dir, ); if show_artifact_paths { println!("Saved contract artifact to: {}", artifact_path.display()); From d08b7b93a981f2e01a3d9754b194c5565ad3a7c2 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:00:25 +0100 Subject: [PATCH 41/57] fix: use proper serialization in `AbiValue` (#5270) # Description ## Problem\* Resolves slack issue https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1718697469796579?thread_ts=1718697265.759469&cid=C02M7VC7TN0 ## Summary\* Looks like when this was added the author used the display method rather than one for serialization. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_driver/src/abi_gen.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index c71474d7731..300a0592e39 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use acvm::acir::circuit::ErrorSelector; +use acvm::AcirField; use iter_extended::vecmap; use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, @@ -197,9 +198,7 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres }, HirLiteral::Bool(value) => AbiValue::Boolean { value }, HirLiteral::Str(value) => AbiValue::String { value }, - HirLiteral::Integer(field, sign) => { - AbiValue::Integer { value: field.to_string(), sign } - } + HirLiteral::Integer(field, sign) => AbiValue::Integer { value: field.to_hex(), sign }, _ => unreachable!("Literal cannot be used in the abi"), }, _ => unreachable!("Type cannot be used in the abi {:?}", expression), From 6599f5660a7a209a33a2a1528379c7e4743525db Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:24:32 +0100 Subject: [PATCH 42/57] chore: replace logical operators with bitwise in `DebugToString` (#5236) # Description ## Problem\* Resolves ## Summary\* We're printing `&&` and `||` in place of `&` and `|` in this code so I've updated this to match the proper operator. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 075a776572e..b258905d657 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -59,8 +59,8 @@ impl DebugToString for BrilligBinaryOp { BrilligBinaryOp::UnsignedDiv => "/".into(), BrilligBinaryOp::LessThan => "<".into(), BrilligBinaryOp::LessThanEquals => "<=".into(), - BrilligBinaryOp::And => "&&".into(), - BrilligBinaryOp::Or => "||".into(), + BrilligBinaryOp::And => "&".into(), + BrilligBinaryOp::Or => "|".into(), BrilligBinaryOp::Xor => "^".into(), BrilligBinaryOp::Shl => "<<".into(), BrilligBinaryOp::Shr => ">>".into(), From 7445efb05165bf7df2f9dfe325abbc42f839364c Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:33:58 +0200 Subject: [PATCH 43/57] feat: add support for wildcard types (#5275) # Description ## Problem\* Resolves #5183 ## Summary\* Add support for wildcard type: `_` ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [X] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_frontend/src/elaborator/types.rs | 4 +++- .../src/hir/resolution/resolver.rs | 1 + docs/docs/noir/concepts/data_types/index.md | 8 +++++++ .../wildcard_type/Nargo.toml | 6 +++++ .../wildcard_type/Prover.toml | 1 + .../wildcard_type/src/main.nr | 23 +++++++++++++++++++ tooling/nargo_cli/build.rs | 18 +++++++++++---- 7 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 test_programs/execution_success/wildcard_type/Nargo.toml create mode 100644 test_programs/execution_success/wildcard_type/Prover.toml create mode 100644 test_programs/execution_success/wildcard_type/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index a9571a6669d..e3f198e68bd 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -11,7 +11,7 @@ use crate::{ def_map::ModuleDefId, resolution::{ errors::ResolverError, - resolver::{verify_mutable_reference, SELF_TYPE_NAME}, + resolver::{verify_mutable_reference, SELF_TYPE_NAME, WILDCARD_TYPE}, }, type_check::{Source, TypeCheckError}, }, @@ -146,6 +146,8 @@ impl<'context> Elaborator<'context> { } return self_type; } + } else if name == WILDCARD_TYPE { + return self.interner.next_type_variable(); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 4583125d2a2..f8db9730f88 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -64,6 +64,7 @@ use super::errors::{PubPosition, ResolverError}; use super::import::PathResolution; pub const SELF_TYPE_NAME: &str = "Self"; +pub const WILDCARD_TYPE: &str = "_"; type Scope = GenericScope; type ScopeTree = GenericScopeTree; diff --git a/docs/docs/noir/concepts/data_types/index.md b/docs/docs/noir/concepts/data_types/index.md index 357813c147a..3eadb2dc8a4 100644 --- a/docs/docs/noir/concepts/data_types/index.md +++ b/docs/docs/noir/concepts/data_types/index.md @@ -105,6 +105,14 @@ type Bad2 = Bad1; // ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 ``` +## Wildcard Type +Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. + +```rust +let a: [_; 4] = foo(b); +``` + + ### BigInt You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/test_programs/execution_success/wildcard_type/Nargo.toml b/test_programs/execution_success/wildcard_type/Nargo.toml new file mode 100644 index 00000000000..e3d7fc636af --- /dev/null +++ b/test_programs/execution_success/wildcard_type/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wildcard_type" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/wildcard_type/Prover.toml b/test_programs/execution_success/wildcard_type/Prover.toml new file mode 100644 index 00000000000..c7c8371dfa4 --- /dev/null +++ b/test_programs/execution_success/wildcard_type/Prover.toml @@ -0,0 +1 @@ +enable = [4,7] diff --git a/test_programs/execution_success/wildcard_type/src/main.nr b/test_programs/execution_success/wildcard_type/src/main.nr new file mode 100644 index 00000000000..c27f9987c48 --- /dev/null +++ b/test_programs/execution_success/wildcard_type/src/main.nr @@ -0,0 +1,23 @@ +struct bar { + enable: [bool; 4], + data: [Field; 2], + pad: u32, +} + +fn main(enable: [Field; 2]) -> pub [Field; 4] { + let mut result = [0; 4]; + let a: [_; 4] = foo(enable[1]); + for i in 0..4 { + result[i] = a[i].data[i % 2]; + } + result +} + +fn foo(x: Field) -> [bar; 4] { + [ + bar { enable: [true, true, false, false], data: [x, x + 1], pad: 0 }, + bar { enable: [true, false, false, false], data: [x + 2, x + 7], pad: 0 }, + bar { enable: [true, true, false, true], data: [x + 3, x + 5], pad: 0 }, + bar { enable: [false, false, false, false], data: [x + 4, x - 1], pad: 0 } + ] +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index d2822ba11ab..ec750236806 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -58,10 +58,10 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ "is_unconstrained", ]; -/// Certain comptime features are only available in the elaborator. +/// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_COMPTIME_TESTS: [&str; 1] = ["macros"]; +const IGNORED_NEW_FEATURE_TESTS: [&str; 2] = ["macros", "wildcard_type"]; fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; @@ -82,11 +82,16 @@ fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) let brillig_ignored = if IGNORED_BRILLIG_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" }; + let new_features_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; write!( test_file, r#" -#[test] +#[test]{new_features_ignored} fn execution_success_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); @@ -286,8 +291,11 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa }; let test_dir = &test_dir.path(); - let comptime_ignored = - if IGNORED_COMPTIME_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" }; + let comptime_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; write!( test_file, From 963b9a3ac1e77f5ac97561d177ae8a6da9b355a9 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:05:23 +0100 Subject: [PATCH 44/57] chore: create separate crate just for noir artifacts (#5162) # Description ## Problem\* Resolves ## Summary\* This PR splits off the definitions for Noir's build artifacts from the `nargo` crate which adds a bunch of high-level logic. This makes it easier to pull in and work on these types without having to pull in the whole compiler. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 20 +++++++++++++++- Cargo.toml | 2 ++ compiler/wasm/Cargo.toml | 1 + compiler/wasm/src/compile.rs | 6 ++--- tooling/debugger/Cargo.toml | 1 + tooling/debugger/src/context.rs | 2 +- tooling/debugger/src/dap.rs | 2 +- tooling/debugger/src/foreign_calls.rs | 6 ++--- tooling/debugger/src/lib.rs | 2 +- tooling/debugger/src/repl.rs | 3 ++- tooling/debugger/src/source_code_printer.rs | 4 ++-- tooling/lsp/Cargo.toml | 1 + tooling/lsp/src/requests/profile_run.rs | 6 ++--- tooling/nargo/Cargo.toml | 3 +-- tooling/nargo/src/lib.rs | 1 - tooling/nargo_cli/Cargo.toml | 1 + tooling/nargo_cli/src/cli/debug_cmd.rs | 2 +- tooling/nargo_cli/src/cli/execute_cmd.rs | 2 +- tooling/nargo_cli/src/cli/fs/program.rs | 2 +- tooling/nargo_cli/src/cli/info_cmd.rs | 6 ++--- tooling/noirc_artifacts/Cargo.toml | 24 +++++++++++++++++++ .../src}/contract.rs | 0 .../src}/debug.rs | 2 +- .../src}/debug_vars.rs | 0 .../mod.rs => noirc_artifacts/src/lib.rs} | 6 +++++ .../src}/program.rs | 0 26 files changed, 75 insertions(+), 30 deletions(-) create mode 100644 tooling/noirc_artifacts/Cargo.toml rename tooling/{nargo/src/artifacts => noirc_artifacts/src}/contract.rs (100%) rename tooling/{nargo/src/artifacts => noirc_artifacts/src}/debug.rs (99%) rename tooling/{nargo/src/artifacts => noirc_artifacts/src}/debug_vars.rs (100%) rename tooling/{nargo/src/artifacts/mod.rs => noirc_artifacts/src/lib.rs} (73%) rename tooling/{nargo/src/artifacts => noirc_artifacts/src}/program.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 9a54944933e..16988432243 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2473,7 +2473,6 @@ name = "nargo" version = "0.31.0" dependencies = [ "acvm", - "codespan-reporting", "fm", "iter-extended", "jsonrpc", @@ -2520,6 +2519,7 @@ dependencies = [ "noir_debugger", "noir_lsp", "noirc_abi", + "noirc_artifacts", "noirc_driver", "noirc_errors", "noirc_frontend", @@ -2649,6 +2649,7 @@ dependencies = [ "easy-repl", "fm", "nargo", + "noirc_artifacts", "noirc_driver", "noirc_errors", "noirc_frontend", @@ -2685,6 +2686,7 @@ dependencies = [ "nargo", "nargo_fmt", "nargo_toml", + "noirc_artifacts", "noirc_driver", "noirc_errors", "noirc_frontend", @@ -2710,6 +2712,7 @@ dependencies = [ "gloo-utils", "js-sys", "nargo", + "noirc_artifacts", "noirc_driver", "noirc_errors", "noirc_evaluator", @@ -2761,6 +2764,21 @@ dependencies = [ name = "noirc_arena" version = "0.31.0" +[[package]] +name = "noirc_artifacts" +version = "0.31.0" +dependencies = [ + "acvm", + "codespan-reporting", + "fm", + "noirc_abi", + "noirc_driver", + "noirc_errors", + "noirc_printable_type", + "serde", + "tempfile", +] + [[package]] name = "noirc_driver" version = "0.31.0" diff --git a/Cargo.toml b/Cargo.toml index 8a4c94d8876..5fc9936de91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "tooling/nargo_fmt", "tooling/nargo_cli", "tooling/nargo_toml", + "tooling/noirc_artifacts", "tooling/noirc_abi", "tooling/noirc_abi_wasm", "tooling/acvm_cli", @@ -74,6 +75,7 @@ nargo_toml = { path = "tooling/nargo_toml" } noir_lsp = { path = "tooling/lsp" } noir_debugger = { path = "tooling/debugger" } noirc_abi = { path = "tooling/noirc_abi" } +noirc_artifacts = { path = "tooling/noirc_artifacts" } bb_abstraction_leaks = { path = "tooling/bb_abstraction_leaks" } acvm_cli = { path = "tooling/acvm_cli" } diff --git a/compiler/wasm/Cargo.toml b/compiler/wasm/Cargo.toml index 03e59b3e269..20272dfeecb 100644 --- a/compiler/wasm/Cargo.toml +++ b/compiler/wasm/Cargo.toml @@ -20,6 +20,7 @@ noirc_driver.workspace = true noirc_frontend = { workspace = true, features = ["bn254"] } noirc_errors.workspace = true noirc_evaluator.workspace = true +noirc_artifacts.workspace = true wasm-bindgen.workspace = true serde.workspace = true js-sys.workspace = true diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index f1495e0b438..59b0e00e49f 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -2,10 +2,8 @@ use acvm::acir::circuit::ExpressionWidth; use fm::FileManager; use gloo_utils::format::JsValueSerdeExt; use js_sys::{JsString, Object}; -use nargo::{ - artifacts::{contract::ContractArtifact, program::ProgramArtifact}, - parse_all, -}; +use nargo::parse_all; +use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; use noirc_driver::{ add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, CompileOptions, }; diff --git a/tooling/debugger/Cargo.toml b/tooling/debugger/Cargo.toml index a3bf12f5368..05b28f9d95a 100644 --- a/tooling/debugger/Cargo.toml +++ b/tooling/debugger/Cargo.toml @@ -18,6 +18,7 @@ noirc_frontend.workspace = true noirc_printable_type.workspace = true noirc_errors.workspace = true noirc_driver.workspace = true +noirc_artifacts.workspace = true thiserror.workspace = true codespan-reporting.workspace = true dap.workspace = true diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 4436ce0ec3d..cb36988bf0b 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -10,9 +10,9 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; -use nargo::artifacts::debug::{DebugArtifact, StackFrame}; use nargo::errors::{ExecutionError, Location}; use nargo::NargoError; +use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; use std::collections::BTreeMap; diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 40b77f0ad2a..77abf3093cd 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -24,7 +24,7 @@ use dap::types::{ Breakpoint, DisassembledInstruction, Scope, Source, StackFrame, SteppingGranularity, StoppedEventReason, Thread, Variable, }; -use nargo::artifacts::debug::DebugArtifact; +use noirc_artifacts::debug::DebugArtifact; use fm::FileId; use noirc_driver::CompiledProgram; diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 03b1a35dfa5..62443d4065c 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -3,10 +3,8 @@ use acvm::{ pwg::ForeignCallWaitInfo, AcirField, FieldElement, }; -use nargo::{ - artifacts::debug::{DebugArtifact, DebugVars, StackFrame}, - ops::{DefaultForeignCallExecutor, ForeignCallExecutor}, -}; +use nargo::ops::{DefaultForeignCallExecutor, ForeignCallExecutor}; +use noirc_artifacts::debug::{DebugArtifact, DebugVars, StackFrame}; use noirc_errors::debug_info::{DebugFnId, DebugVarId}; use noirc_printable_type::ForeignCallError; diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 9168a6228f0..9d0059ee495 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -13,7 +13,7 @@ use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use acvm::{BlackBoxFunctionSolver, FieldElement}; -use nargo::artifacts::debug::DebugArtifact; +use noirc_artifacts::debug::DebugArtifact; use nargo::NargoError; use noirc_driver::CompiledProgram; diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 07f9333d51c..7d8c6e0947d 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -5,9 +5,10 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::{BlackBoxFunctionSolver, FieldElement}; +use nargo::NargoError; use crate::foreign_calls::DefaultDebugForeignCallExecutor; -use nargo::{artifacts::debug::DebugArtifact, NargoError}; +use noirc_artifacts::debug::DebugArtifact; use easy_repl::{command, CommandStatus, Repl}; use noirc_printable_type::PrintableValueDisplay; diff --git a/tooling/debugger/src/source_code_printer.rs b/tooling/debugger/src/source_code_printer.rs index ad511563477..e9586b786bd 100644 --- a/tooling/debugger/src/source_code_printer.rs +++ b/tooling/debugger/src/source_code_printer.rs @@ -1,5 +1,5 @@ use codespan_reporting::files::Files; -use nargo::artifacts::debug::DebugArtifact; +use noirc_artifacts::debug::DebugArtifact; use noirc_errors::Location; use owo_colors::OwoColorize; use std::ops::Range; @@ -224,7 +224,7 @@ mod tests { use crate::source_code_printer::PrintedLine::Content; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; - use nargo::artifacts::debug::DebugArtifact; + use noirc_artifacts::debug::DebugArtifact; use noirc_errors::{debug_info::DebugInfo, Location, Span}; use std::collections::BTreeMap; use std::ops::Range; diff --git a/tooling/lsp/Cargo.toml b/tooling/lsp/Cargo.toml index a599b096e52..ac3e3b1d30a 100644 --- a/tooling/lsp/Cargo.toml +++ b/tooling/lsp/Cargo.toml @@ -19,6 +19,7 @@ nargo_toml.workspace = true noirc_driver.workspace = true noirc_errors.workspace = true noirc_frontend.workspace = true +noirc_artifacts.workspace = true serde.workspace = true serde_json.workspace = true tower.workspace = true diff --git a/tooling/lsp/src/requests/profile_run.rs b/tooling/lsp/src/requests/profile_run.rs index 7d06bc87c85..57bc3299455 100644 --- a/tooling/lsp/src/requests/profile_run.rs +++ b/tooling/lsp/src/requests/profile_run.rs @@ -5,11 +5,9 @@ use std::{ use acvm::acir::circuit::ExpressionWidth; use async_lsp::{ErrorCode, ResponseError}; -use nargo::{ - artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager, - ops::report_errors, -}; +use nargo::{insert_all_files_for_workspace_into_file_manager, ops::report_errors}; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noirc_artifacts::debug::DebugArtifact; use noirc_driver::{ file_manager_with_stdlib, CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING, }; diff --git a/tooling/nargo/Cargo.toml b/tooling/nargo/Cargo.toml index 8abec267d20..b0cf1cfcbb1 100644 --- a/tooling/nargo/Cargo.toml +++ b/tooling/nargo/Cargo.toml @@ -18,13 +18,12 @@ noirc_errors.workspace = true noirc_frontend.workspace = true noirc_printable_type.workspace = true iter-extended.workspace = true -serde.workspace = true thiserror.workspace = true -codespan-reporting.workspace = true tracing.workspace = true rayon = "1.8.0" jsonrpc.workspace = true rand.workspace = true +serde.workspace = true [dev-dependencies] # TODO: This dependency is used to generate unit tests for `get_all_paths_in_dir` diff --git a/tooling/nargo/src/lib.rs b/tooling/nargo/src/lib.rs index 3deced041f8..c0c7602d14d 100644 --- a/tooling/nargo/src/lib.rs +++ b/tooling/nargo/src/lib.rs @@ -7,7 +7,6 @@ //! This name was used because it sounds like `cargo` and //! Noir Package Manager abbreviated is npm, which is already taken. -pub mod artifacts; pub mod constants; pub mod errors; pub mod ops; diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index a9946c8700c..c3beafb7db5 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -32,6 +32,7 @@ noirc_driver.workspace = true noirc_frontend = { workspace = true, features = ["bn254"] } noirc_abi.workspace = true noirc_errors.workspace = true +noirc_artifacts.workspace = true acvm = { workspace = true, features = ["bn254"] } bn254_blackbox_solver.workspace = true toml.workspace = true diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 086dddc27e5..778009bf791 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -6,7 +6,6 @@ use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use fm::FileManager; -use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::CompileError; use nargo::ops::{compile_program, compile_program_with_debug_instrumenter, report_errors}; @@ -16,6 +15,7 @@ use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; use noirc_abi::InputMap; +use noirc_artifacts::debug::DebugArtifact; use noirc_driver::{ file_manager_with_stdlib, CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index b548336275b..cf9dc1141a1 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -3,7 +3,6 @@ use acvm::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; -use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; use nargo::ops::DefaultForeignCallExecutor; @@ -11,6 +10,7 @@ use nargo::package::Package; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; use noirc_abi::InputMap; +use noirc_artifacts::debug::DebugArtifact; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::graph::CrateName; diff --git a/tooling/nargo_cli/src/cli/fs/program.rs b/tooling/nargo_cli/src/cli/fs/program.rs index ba017651667..caeaafd4ab3 100644 --- a/tooling/nargo_cli/src/cli/fs/program.rs +++ b/tooling/nargo_cli/src/cli/fs/program.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use nargo::artifacts::{contract::ContractArtifact, program::ProgramArtifact}; +use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; use noirc_frontend::graph::CrateName; use crate::errors::FilesystemError; diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index 7c50e907dc9..3759fb31c76 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -3,11 +3,9 @@ use std::collections::HashMap; use acvm::acir::circuit::ExpressionWidth; use clap::Args; use iter_extended::vecmap; -use nargo::{ - artifacts::{debug::DebugArtifact, program::ProgramArtifact}, - package::Package, -}; +use nargo::package::Package; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noirc_artifacts::{debug::DebugArtifact, program::ProgramArtifact}; use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::{debug_info::OpCodesCount, Location}; use noirc_frontend::graph::CrateName; diff --git a/tooling/noirc_artifacts/Cargo.toml b/tooling/noirc_artifacts/Cargo.toml new file mode 100644 index 00000000000..4249604f949 --- /dev/null +++ b/tooling/noirc_artifacts/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "noirc_artifacts" +description = "Definitions of Nargo's build artifacts" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +acvm.workspace = true +fm.workspace = true +noirc_abi.workspace = true +noirc_driver.workspace = true +noirc_errors.workspace = true +noirc_printable_type.workspace = true +serde.workspace = true +codespan-reporting.workspace = true + + +[dev-dependencies] +tempfile.workspace = true diff --git a/tooling/nargo/src/artifacts/contract.rs b/tooling/noirc_artifacts/src/contract.rs similarity index 100% rename from tooling/nargo/src/artifacts/contract.rs rename to tooling/noirc_artifacts/src/contract.rs diff --git a/tooling/nargo/src/artifacts/debug.rs b/tooling/noirc_artifacts/src/debug.rs similarity index 99% rename from tooling/nargo/src/artifacts/debug.rs rename to tooling/noirc_artifacts/src/debug.rs index 21102c40fcf..11a3e1c4dd7 100644 --- a/tooling/nargo/src/artifacts/debug.rs +++ b/tooling/noirc_artifacts/src/debug.rs @@ -186,7 +186,7 @@ impl<'a> Files<'a> for DebugArtifact { #[cfg(test)] mod tests { - use crate::artifacts::debug::DebugArtifact; + use crate::debug::DebugArtifact; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; use noirc_errors::{debug_info::DebugInfo, Location, Span}; diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/noirc_artifacts/src/debug_vars.rs similarity index 100% rename from tooling/nargo/src/artifacts/debug_vars.rs rename to tooling/noirc_artifacts/src/debug_vars.rs diff --git a/tooling/nargo/src/artifacts/mod.rs b/tooling/noirc_artifacts/src/lib.rs similarity index 73% rename from tooling/nargo/src/artifacts/mod.rs rename to tooling/noirc_artifacts/src/lib.rs index c7b3736f90b..77873ed9409 100644 --- a/tooling/nargo/src/artifacts/mod.rs +++ b/tooling/noirc_artifacts/src/lib.rs @@ -1,8 +1,14 @@ +#![forbid(unsafe_code)] +#![warn(unused_crate_dependencies, unused_extern_crates)] +#![warn(unreachable_pub)] +#![warn(clippy::semicolon_if_nothing_returned)] + //! This module defines the structure of Nargo's different compilation artifacts. //! //! These artifacts are intended to remain independent of any applications being built on top of Noir. //! Should any projects require/desire a different artifact format, it's expected that they will write a transformer //! to generate them using these artifacts as a starting point. + pub mod contract; pub mod debug; mod debug_vars; diff --git a/tooling/nargo/src/artifacts/program.rs b/tooling/noirc_artifacts/src/program.rs similarity index 100% rename from tooling/nargo/src/artifacts/program.rs rename to tooling/noirc_artifacts/src/program.rs From 9db65d8706ac8b67921f2a73163ab8bee3dfb4e8 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:09:26 +0100 Subject: [PATCH 45/57] feat: implement comptime support for `as_slice` builtin (#5276) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5274 ## Summary\* This PR implements the `as_slice` builtin function for the comptime interpreter. I've also fixed an issue where due to builtin/foreign/oracle functions having an early return, we don't call `exit_function` which results in the scopes being left in an inconsistent state. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/comptime/interpreter.rs | 5 +++-- .../src/hir/comptime/interpreter/builtin.rs | 15 ++++++++++++++- .../comptime_as_slice/Nargo.toml | 7 +++++++ .../comptime_as_slice/src/main.nr | 9 +++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test_programs/compile_success_empty/comptime_as_slice/Nargo.toml create mode 100644 test_programs/compile_success_empty/comptime_as_slice/src/main.nr diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index c473ac7f8a6..07da3398593 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -62,8 +62,6 @@ impl<'a> Interpreter<'a> { arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - let previous_state = self.enter_function(); - let meta = self.interner.function_meta(&function); if meta.parameters.len() != arguments.len() { return Err(InterpreterError::ArgumentCountMismatch { @@ -78,6 +76,8 @@ impl<'a> Interpreter<'a> { } let parameters = meta.parameters.0.clone(); + let previous_state = self.enter_function(); + for ((parameter, typ, _), (argument, arg_location)) in parameters.iter().zip(arguments) { self.define_pattern(parameter, typ, argument, arg_location)?; } @@ -102,6 +102,7 @@ impl<'a> Interpreter<'a> { if let Some(builtin) = func_attrs.builtin() { match builtin.as_str() { "array_len" => builtin::array_len(&arguments), + "as_slice" => builtin::as_slice(arguments), _ => { let item = format!("Evaluation for builtin function {builtin}"); Err(InterpreterError::Unimplemented { item, location }) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 3e5ad207719..9216ba271c1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1,6 +1,9 @@ use noirc_errors::Location; -use crate::hir::comptime::{errors::IResult, Value}; +use crate::{ + hir::comptime::{errors::IResult, Value}, + Type, +}; pub(super) fn array_len(arguments: &[(Value, Location)]) -> IResult { assert_eq!(arguments.len(), 1, "ICE: `array_len` should only receive a single argument"); @@ -10,3 +13,13 @@ pub(super) fn array_len(arguments: &[(Value, Location)]) -> IResult { _ => unreachable!("ICE: Cannot query length of types other than arrays or slices"), } } + +pub(super) fn as_slice(mut arguments: Vec<(Value, Location)>) -> IResult { + assert_eq!(arguments.len(), 1, "ICE: `as_slice` should only receive a single argument"); + let (array, _) = arguments.pop().unwrap(); + match array { + Value::Array(values, Type::Array(_, typ)) => Ok(Value::Slice(values, Type::Slice(typ))), + // Type checking should prevent this branch being taken. + _ => unreachable!("ICE: Cannot convert types other than arrays into slices"), + } +} diff --git a/test_programs/compile_success_empty/comptime_as_slice/Nargo.toml b/test_programs/compile_success_empty/comptime_as_slice/Nargo.toml new file mode 100644 index 00000000000..90c67b07b2b --- /dev/null +++ b/test_programs/compile_success_empty/comptime_as_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "array_to_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.24.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/comptime_as_slice/src/main.nr b/test_programs/compile_success_empty/comptime_as_slice/src/main.nr new file mode 100644 index 00000000000..07c5e344cc2 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_as_slice/src/main.nr @@ -0,0 +1,9 @@ +fn main() { + comptime + { + let ws: [Field; 3] = [1; 3]; + let ws_as_slice: [Field] = ws.as_slice(); + + assert_eq(ws[0], ws_as_slice[0]); + } +} From 6cbe6a0c830b2992666e0f9bdbc8f66ec41eed84 Mon Sep 17 00:00:00 2001 From: jfecher Date: Tue, 18 Jun 2024 12:32:54 -0500 Subject: [PATCH 46/57] feat: Run `comptime` code from annotations on a type definition (#5256) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5255 ## Summary\* Implements the ability to run comptime code when an annotation is put on a type definition. This annotation must resolve to a comptime function in scope, which is then called with the type definition as an argument. There are currently no API functions to actually do anything with a `TypeDefinition` object. The plan is to add functions in the future to inspect or add fields & generics. ```rs #[print_type] struct Foo { bar: Field, } comptime fn print_type(typ: TypeDefinition) { println("hello from print_type_name at compile-time"); println(typ); // only prints "(type definition)" currently } fn main(){} ``` ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Maxim Vezenov --- compiler/noirc_driver/src/abi_gen.rs | 2 +- compiler/noirc_frontend/src/ast/mod.rs | 4 +- .../src/elaborator/expressions.rs | 9 +- compiler/noirc_frontend/src/elaborator/mod.rs | 133 +++++++++++++++--- .../noirc_frontend/src/elaborator/types.rs | 4 +- .../src/hir/comptime/interpreter.rs | 6 +- .../noirc_frontend/src/hir/comptime/value.rs | 14 +- .../src/hir/resolution/errors.rs | 20 ++- .../src/hir/resolution/resolver.rs | 4 +- .../noirc_frontend/src/hir/type_check/expr.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 43 ++++-- compiler/noirc_frontend/src/lexer/token.rs | 9 ++ .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/node_interner.rs | 5 +- .../noirc_frontend/src/parser/parser/types.rs | 28 +++- .../type_definition_annotation/Nargo.toml | 7 + .../type_definition_annotation/src/main.nr | 8 ++ tooling/nargo_cli/build.rs | 15 +- tooling/nargo_fmt/src/rewrite/typ.rs | 2 +- tooling/nargo_fmt/tests/expected/impl.nr | 12 +- tooling/nargo_fmt/tests/input/impl.nr | 14 +- 21 files changed, 269 insertions(+), 74 deletions(-) create mode 100644 test_programs/compile_failure/type_definition_annotation/Nargo.toml create mode 100644 test_programs/compile_failure/type_definition_annotation/src/main.nr diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 300a0592e39..d0b33945f40 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -104,7 +104,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) - | Type::Expr + | Type::Quoted(_) | Type::Slice(_) | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 3492df73103..a1ae349b537 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -117,7 +117,7 @@ pub enum UnresolvedTypeData { ), // The type of quoted code for metaprogramming - Expr, + Quoted(crate::QuotedType), Unspecified, // This is for when the user declares a variable without specifying it's type Error, @@ -216,7 +216,7 @@ impl std::fmt::Display for UnresolvedTypeData { } } MutableReference(element) => write!(f, "&mut {element}"), - Expr => write!(f, "Expr"), + Quoted(quoted) => write!(f, "{}", quoted), Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index a57690878dc..d6ad752b67a 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -28,7 +28,7 @@ use crate::{ MethodCallExpression, PrefixExpression, }, node_interner::{DefinitionKind, ExprId, FuncId}, - Shared, StructType, Type, + QuotedType, Shared, StructType, Type, }; use super::Elaborator; @@ -650,7 +650,7 @@ impl<'context> Elaborator<'context> { let mut unquoted_exprs = Vec::new(); self.find_unquoted_exprs_in_block(&mut block, &mut unquoted_exprs); let quoted = HirQuoted { quoted_block: block, unquoted_exprs }; - (HirExpression::Quote(quoted), Type::Expr) + (HirExpression::Quote(quoted), Type::Quoted(QuotedType::Expr)) } fn elaborate_comptime_block(&mut self, block: BlockExpression, span: Span) -> (ExprId, Type) { @@ -716,9 +716,8 @@ impl<'context> Elaborator<'context> { location: Location, return_type: Type, ) -> Option<(HirExpression, Type)> { - self.unify(&return_type, &Type::Expr, || TypeCheckError::MacroReturningNonExpr { - typ: return_type.clone(), - span: location.span, + self.unify(&return_type, &Type::Quoted(QuotedType::Expr), || { + TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), span: location.span } }); let function = match self.try_get_comptime_function(func, location) { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 0b7e5a58849..053de6e1735 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ ast::{FunctionKind, UnresolvedTraitConstraint}, hir::{ - comptime::{self, Interpreter}, + comptime::{self, Interpreter, Value}, def_collector::{ dc_crate::{ filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal, @@ -205,10 +205,22 @@ impl<'context> Elaborator<'context> { pub fn elaborate( context: &'context mut Context, crate_id: CrateId, - mut items: CollectedItems, + items: CollectedItems, ) -> Vec<(CompilationError, FileId)> { let mut this = Self::new(context, crate_id); + // Filter out comptime items to execute their functions first if needed. + // This step is why comptime items can only refer to other comptime items + // in the same crate, but can refer to any item in dependencies. Trying to + // run these at the same time as other items would lead to them seeing empty + // function bodies from functions that have yet to be elaborated. + let (comptime_items, runtime_items) = Self::filter_comptime_items(items); + this.elaborate_items(comptime_items); + this.elaborate_items(runtime_items); + this.errors + } + + fn elaborate_items(&mut self, mut items: CollectedItems) { // We must first resolve and intern the globals before we can resolve any stmts inside each function. // Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope // @@ -216,58 +228,56 @@ impl<'context> Elaborator<'context> { // the values of integer globals as numeric generics. let (literal_globals, non_literal_globals) = filter_literal_globals(items.globals); for global in non_literal_globals { - this.unresolved_globals.insert(global.global_id, global); + self.unresolved_globals.insert(global.global_id, global); } for global in literal_globals { - this.elaborate_global(global); + self.elaborate_global(global); } for (alias_id, alias) in items.type_aliases { - this.define_type_alias(alias_id, alias); + self.define_type_alias(alias_id, alias); } - this.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); - this.collect_traits(items.traits); + self.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); + self.collect_traits(items.traits); // Must resolve structs before we resolve globals. - this.collect_struct_definitions(items.types); + self.collect_struct_definitions(items.types); // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be // done during def collection since we need to be able to resolve the type of // the impl since that determines the module we should collect into. for ((_self_type, module), impls) in &mut items.impls { - this.collect_impls(*module, impls); + self.collect_impls(*module, impls); } // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. for trait_impl in &mut items.trait_impls { - this.collect_trait_impl(trait_impl); + self.collect_trait_impl(trait_impl); } // We must wait to resolve non-literal globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to ensure they are valid. - while let Some((_, global)) = this.unresolved_globals.pop_first() { - this.elaborate_global(global); + while let Some((_, global)) = self.unresolved_globals.pop_first() { + self.elaborate_global(global); } for functions in items.functions { - this.elaborate_functions(functions); + self.elaborate_functions(functions); } for impls in items.impls.into_values() { - this.elaborate_impls(impls); + self.elaborate_impls(impls); } for trait_impl in items.trait_impls { - this.elaborate_trait_impl(trait_impl); + self.elaborate_trait_impl(trait_impl); } - let cycle_errors = this.interner.check_for_dependency_cycles(); - this.errors.extend(cycle_errors); - this.errors + self.errors.extend(self.interner.check_for_dependency_cycles()); } /// Runs `f` and if it modifies `self.generics`, `self.generics` is truncated @@ -1085,15 +1095,20 @@ impl<'context> Elaborator<'context> { // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. - for (type_id, typ) in structs { + for (type_id, mut typ) in structs { self.file = typ.file_id; self.local_module = typ.module_id; + + let attributes = std::mem::take(&mut typ.struct_def.attributes); + let span = typ.struct_def.span; let (generics, fields) = self.resolve_struct_fields(typ.struct_def, type_id); self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); struct_def.generics = generics; }); + + self.run_comptime_attributes_on_struct(attributes, type_id, span); } // Check whether the struct fields have nested slices @@ -1117,6 +1132,38 @@ impl<'context> Elaborator<'context> { } } + fn run_comptime_attributes_on_struct( + &mut self, + attributes: Vec, + struct_id: StructId, + span: Span, + ) { + for attribute in attributes { + if let SecondaryAttribute::Custom(name) = attribute { + match self.lookup_global(Path::from_single(name, span)) { + Ok(id) => { + let definition = self.interner.definition(id); + if let DefinitionKind::Function(function) = &definition.kind { + let function = *function; + let mut interpreter = + Interpreter::new(self.interner, &mut self.comptime_scopes); + + let location = Location::new(span, self.file); + let arguments = vec![(Value::TypeDefinition(struct_id), location)]; + let result = interpreter.call_function(function, arguments, location); + if let Err(error) = result { + self.errors.push(error.into_compilation_error_pair()); + } + } else { + self.push_err(ResolverError::NonFunctionInAnnotation { span }); + } + } + Err(_) => self.push_err(ResolverError::UnknownAnnotation { span }), + } + } + } + } + pub fn resolve_struct_fields( &mut self, unresolved: NoirStruct, @@ -1261,4 +1308,52 @@ impl<'context> Elaborator<'context> { }); } } + + /// Filters out comptime items from non-comptime items. + /// Returns a pair of (comptime items, non-comptime items) + fn filter_comptime_items(mut items: CollectedItems) -> (CollectedItems, CollectedItems) { + let mut function_sets = Vec::with_capacity(items.functions.len()); + let mut comptime_function_sets = Vec::new(); + + for function_set in items.functions { + let mut functions = Vec::with_capacity(function_set.functions.len()); + let mut comptime_functions = Vec::new(); + + for function in function_set.functions { + if function.2.def.is_comptime { + comptime_functions.push(function); + } else { + functions.push(function); + } + } + + let file_id = function_set.file_id; + let self_type = function_set.self_type; + let trait_id = function_set.trait_id; + + if !comptime_functions.is_empty() { + comptime_function_sets.push(UnresolvedFunctions { + functions: comptime_functions, + file_id, + trait_id, + self_type: self_type.clone(), + }); + } + + function_sets.push(UnresolvedFunctions { functions, file_id, trait_id, self_type }); + } + + let comptime = CollectedItems { + functions: comptime_function_sets, + types: BTreeMap::new(), + type_aliases: BTreeMap::new(), + traits: BTreeMap::new(), + trait_impls: Vec::new(), + globals: Vec::new(), + impls: std::collections::HashMap::new(), + }; + + items.functions = function_sets; + (comptime, items) + } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index e3f198e68bd..fcb7ac94c26 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -72,7 +72,7 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_type_inner(*fields); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } - Expr => Type::Expr, + Quoted(quoted) => Type::Quoted(quoted), Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, @@ -1398,7 +1398,7 @@ impl<'context> Elaborator<'context> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::Expr + | Type::Quoted(_) | Type::Forall(_, _) => (), Type::TraitAsType(_, _, args) => { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 07da3398593..21b6897f5dd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -104,18 +104,18 @@ impl<'a> Interpreter<'a> { "array_len" => builtin::array_len(&arguments), "as_slice" => builtin::as_slice(arguments), _ => { - let item = format!("Evaluation for builtin function {builtin}"); + let item = format!("Comptime evaluation for builtin function {builtin}"); Err(InterpreterError::Unimplemented { item, location }) } } } else if let Some(foreign) = func_attrs.foreign() { - let item = format!("Evaluation for foreign functions like {foreign}"); + let item = format!("Comptime evaluation for foreign functions like {foreign}"); Err(InterpreterError::Unimplemented { item, location }) } else if let Some(oracle) = func_attrs.oracle() { if oracle == "print" { self.print_oracle(arguments) } else { - let item = format!("Evaluation for oracle functions like {oracle}"); + let item = format!("Comptime evaluation for oracle functions like {oracle}"); Err(InterpreterError::Unimplemented { item, location }) } } else { diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 5d91b151218..a75d4fd2b24 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -12,9 +12,10 @@ use crate::{ hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, + StructId, }, node_interner::{ExprId, FuncId}, - Shared, Type, + QuotedType, Shared, Type, }; use rustc_hash::FxHashMap as HashMap; @@ -42,6 +43,7 @@ pub enum Value { Array(Vector, Type), Slice(Vector, Type), Code(Rc), + TypeDefinition(StructId), } impl Value { @@ -70,7 +72,8 @@ impl Value { Value::Struct(_, typ) => return Cow::Borrowed(typ), Value::Array(_, typ) => return Cow::Borrowed(typ), Value::Slice(_, typ) => return Cow::Borrowed(typ), - Value::Code(_) => Type::Expr, + Value::Code(_) => Type::Quoted(QuotedType::Expr), + Value::TypeDefinition(_) => Type::Quoted(QuotedType::TypeDefinition), Value::Pointer(element) => { let element = element.borrow().get_type().into_owned(); Type::MutableReference(Box::new(element)) @@ -172,7 +175,7 @@ impl Value { ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(elements))) } Value::Code(block) => ExpressionKind::Block(unwrap_rc(block)), - Value::Pointer(_) => { + Value::Pointer(_) | Value::TypeDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } }; @@ -273,7 +276,7 @@ impl Value { HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } Value::Code(block) => HirExpression::Unquote(unwrap_rc(block)), - Value::Pointer(_) => { + Value::Pointer(_) | Value::TypeDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } }; @@ -348,7 +351,8 @@ impl Display for Value { let values = vecmap(values, ToString::to_string); write!(f, "&[{}]", values.join(", ")) } - Value::Code(_) => todo!(), + Value::Code(block) => write!(f, "quote {block}"), + Value::TypeDefinition(_) => write!(f, "(type definition)"), } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 2c6461cb77c..dad0149ec73 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -106,6 +106,10 @@ pub enum ResolverError { InvalidSyntaxInMacroCall { span: Span }, #[error("Macros must be comptime functions")] MacroIsNotComptime { span: Span }, + #[error("Annotation name must refer to a comptime function")] + NonFunctionInAnnotation { span: Span }, + #[error("Unknown annotation")] + UnknownAnnotation { span: Span }, } impl ResolverError { @@ -411,13 +415,27 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, - ResolverError::MacroIsNotComptime{ span } => { + ResolverError::MacroIsNotComptime { span } => { Diagnostic::simple_error( "This macro call is to a non-comptime function".into(), "Macro calls must be to comptime functions".into(), *span, ) }, + ResolverError::NonFunctionInAnnotation { span } => { + Diagnostic::simple_error( + "Unknown annotation".into(), + "The name of an annotation must refer to a comptime function".into(), + *span, + ) + }, + ResolverError::UnknownAnnotation { span } => { + Diagnostic::simple_warning( + "Unknown annotation".into(), + "No matching comptime function found in scope".into(), + *span, + ) + }, } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index f8db9730f88..1dcace82372 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -570,7 +570,7 @@ impl<'a> Resolver<'a> { let fields = self.resolve_type_inner(*fields); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } - Expr => Type::Expr, + Quoted(quoted) => Type::Quoted(quoted), Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, @@ -1159,7 +1159,7 @@ impl<'a> Resolver<'a> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::Expr + | Type::Quoted(_) | Type::Forall(_, _) => (), Type::TraitAsType(_, _, args) => { diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 503bbbf79be..46e8db8f5ff 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -307,7 +307,7 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } - HirExpression::Quote(_) => Type::Expr, + HirExpression::Quote(_) => Type::Quoted(crate::QuotedType::Expr), HirExpression::Comptime(block) => self.check_block(block), // Unquote should be inserted & removed by the comptime interpreter. diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a90bd931496..ce36a22cf88 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -105,7 +105,7 @@ pub enum Type { Constant(u32), /// The type of quoted code in macros. This is always a comptime-only type - Expr, + Quoted(QuotedType), /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with @@ -147,7 +147,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) - | Type::Expr + | Type::Quoted(_) | Type::Slice(_) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } @@ -187,6 +187,14 @@ impl Type { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub enum QuotedType { + Expr, + TypeDefinition, + TopLevelItem, + Type, +} + /// A list of TypeVariableIds to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. @@ -644,7 +652,7 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::Forall(_, _) - | Type::Expr => false, + | Type::Quoted(_) => false, Type::TraitAsType(_, _, args) => { args.iter().any(|generic| generic.contains_numeric_typevar(target_id)) @@ -710,7 +718,7 @@ impl Type { | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) - | Type::Expr + | Type::Quoted(_) | Type::Slice(_) | Type::TraitAsType(..) => false, @@ -759,7 +767,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) // TODO: probably can allow code as it is all compile time - | Type::Expr + | Type::Quoted(_) | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { @@ -922,7 +930,7 @@ impl std::fmt::Display for Type { Type::MutableReference(element) => { write!(f, "&mut {element}") } - Type::Expr => write!(f, "Expr"), + Type::Quoted(quoted) => write!(f, "{}", quoted), } } } @@ -954,6 +962,17 @@ impl std::fmt::Display for TypeBinding { } } +impl std::fmt::Display for QuotedType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QuotedType::Expr => write!(f, "Expr"), + QuotedType::TypeDefinition => write!(f, "TypeDefinition"), + QuotedType::TopLevelItem => write!(f, "TopLevelItem"), + QuotedType::Type => write!(f, "Type"), + } + } +} + pub struct UnificationError; impl Type { @@ -1643,7 +1662,7 @@ impl Type { | Type::Bool | Type::Constant(_) | Type::Error - | Type::Expr + | Type::Quoted(_) | Type::Unit => self.clone(), } } @@ -1686,7 +1705,7 @@ impl Type { | Type::Bool | Type::Constant(_) | Type::Error - | Type::Expr + | Type::Quoted(_) | Type::Unit => false, } } @@ -1743,7 +1762,9 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Expr | Error => self.clone(), + FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Quoted(_) | Error => { + self.clone() + } } } @@ -1878,7 +1899,7 @@ impl From<&Type> for PrintableType { Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } - Type::Expr => unreachable!(), + Type::Quoted(_) => unreachable!(), } } } @@ -1963,7 +1984,7 @@ impl std::fmt::Debug for Type { Type::MutableReference(element) => { write!(f, "&mut {element:?}") } - Type::Expr => write!(f, "Expr"), + Type::Quoted(quoted) => write!(f, "{}", quoted), } } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index d204191796c..ec589a816f6 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -865,8 +865,11 @@ pub enum Keyword { String, Struct, Super, + TopLevelItem, Trait, Type, + TypeType, + TypeDefinition, Unchecked, Unconstrained, Use, @@ -910,8 +913,11 @@ impl fmt::Display for Keyword { Keyword::String => write!(f, "str"), Keyword::Struct => write!(f, "struct"), Keyword::Super => write!(f, "super"), + Keyword::TopLevelItem => write!(f, "TopLevelItem"), Keyword::Trait => write!(f, "trait"), Keyword::Type => write!(f, "type"), + Keyword::TypeType => write!(f, "Type"), + Keyword::TypeDefinition => write!(f, "TypeDefinition"), Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), Keyword::Use => write!(f, "use"), @@ -958,8 +964,11 @@ impl Keyword { "str" => Keyword::String, "struct" => Keyword::Struct, "super" => Keyword::Super, + "TopLevelItem" => Keyword::TopLevelItem, "trait" => Keyword::Trait, "type" => Keyword::Type, + "Type" => Keyword::TypeType, + "TypeDefinition" => Keyword::TypeDefinition, "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, "use" => Keyword::Use, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index c6cc1344aae..821ae7e7c4c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1012,7 +1012,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } - HirType::Expr => unreachable!("Tried to translate Code type into runtime code"), + HirType::Quoted(_) => unreachable!("Tried to translate Code type into runtime code"), }) } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5fdce80087a..915093df514 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -17,6 +17,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::QuotedType; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; use crate::hir::resolution::errors::ResolverError; @@ -1805,7 +1806,7 @@ enum TypeMethodKey { Tuple, Function, Generic, - Code, + Quoted(QuotedType), } fn get_type_method_key(typ: &Type) -> Option { @@ -1825,7 +1826,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), Type::NamedGeneric(_, _) => Some(Generic), - Type::Expr => Some(Code), + Type::Quoted(quoted) => Some(Quoted(*quoted)), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 40edc9936d1..a961b32f9d7 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -3,6 +3,7 @@ use super::{ ParserErrorReason, Precedence, }; use crate::ast::{Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; +use crate::QuotedType; use crate::parser::labels::ParsingRuleLabel; use crate::token::{Keyword, Token}; @@ -23,6 +24,9 @@ pub(super) fn parse_type_inner<'a>( bool_type(), string_type(), expr_type(), + type_definition_type(), + top_level_item_type(), + quoted_type(), format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), @@ -69,7 +73,29 @@ pub(super) fn bool_type() -> impl NoirParser { /// This is the type `Expr` - the type of a quoted, untyped expression object used for macros pub(super) fn expr_type() -> impl NoirParser { - keyword(Keyword::Expr).map_with_span(|_, span| UnresolvedTypeData::Expr.with_span(span)) + keyword(Keyword::Expr) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Expr).with_span(span)) +} + +/// This is the type `TypeDefinition` - the type of a quoted type definition +pub(super) fn type_definition_type() -> impl NoirParser { + keyword(Keyword::TypeDefinition).map_with_span(|_, span| { + UnresolvedTypeData::Quoted(QuotedType::TypeDefinition).with_span(span) + }) +} + +/// This is the type `TopLevelItem` - the type of a quoted statement in the top level. +/// E.g. a type definition, trait definition, trait impl, function, etc. +fn top_level_item_type() -> impl NoirParser { + keyword(Keyword::TopLevelItem).map_with_span(|_, span| { + UnresolvedTypeData::Quoted(QuotedType::TopLevelItem).with_span(span) + }) +} + +/// This is the type `Type` - the type of a quoted noir type. +fn quoted_type() -> impl NoirParser { + keyword(Keyword::TypeType) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Type).with_span(span)) } pub(super) fn string_type() -> impl NoirParser { diff --git a/test_programs/compile_failure/type_definition_annotation/Nargo.toml b/test_programs/compile_failure/type_definition_annotation/Nargo.toml new file mode 100644 index 00000000000..dc90816e16b --- /dev/null +++ b/test_programs/compile_failure/type_definition_annotation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "type_definition_annotation" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/type_definition_annotation/src/main.nr b/test_programs/compile_failure/type_definition_annotation/src/main.nr new file mode 100644 index 00000000000..91f9c3a52f4 --- /dev/null +++ b/test_programs/compile_failure/type_definition_annotation/src/main.nr @@ -0,0 +1,8 @@ +#[fail_assert] +struct Foo { x: Field } + +comptime fn fail_assert(_typ: TypeDefinition) { + assert(false); +} + +fn main() {} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index ec750236806..43c277ba03e 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -61,7 +61,8 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ /// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_NEW_FEATURE_TESTS: [&str; 2] = ["macros", "wildcard_type"]; +const IGNORED_NEW_FEATURE_TESTS: [&str; 3] = + ["macros", "wildcard_type", "type_definition_annotation"]; fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; @@ -291,7 +292,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa }; let test_dir = &test_dir.path(); - let comptime_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + let new_feature_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" @@ -300,7 +301,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa write!( test_file, r#" -#[test]{comptime_ignored} +#[test]{new_feature_ignored} fn compile_success_empty_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); @@ -417,10 +418,16 @@ fn generate_compile_failure_tests(test_file: &mut File, test_data_dir: &Path) { }; let test_dir = &test_dir.path(); + let new_feature_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; + write!( test_file, r#" -#[test] +#[test]{new_feature_ignored} fn compile_failure_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index 3ad1d9302be..3eb398346c3 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -64,7 +64,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) | UnresolvedTypeData::Expression(_) | UnresolvedTypeData::String(_) | UnresolvedTypeData::FormatString(_, _) - | UnresolvedTypeData::Expr + | UnresolvedTypeData::Quoted(_) | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span.unwrap()).into(), UnresolvedTypeData::Error => unreachable!(), } diff --git a/tooling/nargo_fmt/tests/expected/impl.nr b/tooling/nargo_fmt/tests/expected/impl.nr index 1c0d4564b5e..ec734b57970 100644 --- a/tooling/nargo_fmt/tests/expected/impl.nr +++ b/tooling/nargo_fmt/tests/expected/impl.nr @@ -1,10 +1,10 @@ -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type { +impl MyType { fn method(self) {} fn method(mut self) {} @@ -12,10 +12,10 @@ impl Type { fn method(&mut self) {} } -impl Type { +impl MyType { fn method(self) {} } -impl Type { +impl MyType { fn method(self) {} } diff --git a/tooling/nargo_fmt/tests/input/impl.nr b/tooling/nargo_fmt/tests/input/impl.nr index 1f111371a43..ea909dfad44 100644 --- a/tooling/nargo_fmt/tests/input/impl.nr +++ b/tooling/nargo_fmt/tests/input/impl.nr @@ -1,10 +1,10 @@ -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type { +impl MyType { fn method(self) {} fn method(mut self) {} @@ -12,10 +12,10 @@ impl Type { fn method(&mut self) {} } -impl Type { +impl MyType { fn method(self) {} } -impl Type { +impl MyType { fn method(self) {} -} \ No newline at end of file +} From e1000176a31140b2abd79c47653cbc4bb1a6808a Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 18 Jun 2024 19:52:45 +0100 Subject: [PATCH 47/57] feat: add fuzzer for Noir programs (#5251) # Description ## Problem\* Step towards #5249 ## Summary\* This PR adds a very simple fuzzer which can be used to find inputs to Noir programs which fail to execute. The motivation for this is to eventually allow `nargo test` to run on noir functions with arguments and automatically fuzz them in a similar fashion to `forge test`. It's currently very hit and miss on how quickly it can zero in on failing cases. For example, the program below is near-unfuzzable currently. ```rust fn main(x: u32, y: u32) { assert(x != y); } ``` ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 14 +++ Cargo.toml | 2 + .../noirc_frontend/src/elaborator/lints.rs | 9 -- compiler/noirc_frontend/src/elaborator/mod.rs | 1 - .../src/hir/resolution/resolver.rs | 10 +- noir_stdlib/src/uint128.nr | 18 +++- tooling/fuzzer/Cargo.toml | 18 ++++ tooling/fuzzer/src/lib.rs | 93 ++++++++++++++++++ tooling/fuzzer/src/strategies/int.rs | 83 ++++++++++++++++ tooling/fuzzer/src/strategies/mod.rs | 97 +++++++++++++++++++ tooling/fuzzer/src/strategies/uint.rs | 68 +++++++++++++ tooling/fuzzer/src/types.rs | 42 ++++++++ tooling/nargo_cli/Cargo.toml | 2 + tooling/nargo_cli/src/cli/test_cmd.rs | 52 ++++++++-- tooling/nargo_cli/tests/stdlib-tests.rs | 57 +++++++++-- tooling/noirc_abi/Cargo.toml | 3 +- 16 files changed, 524 insertions(+), 45 deletions(-) create mode 100644 tooling/fuzzer/Cargo.toml create mode 100644 tooling/fuzzer/src/lib.rs create mode 100644 tooling/fuzzer/src/strategies/int.rs create mode 100644 tooling/fuzzer/src/strategies/mod.rs create mode 100644 tooling/fuzzer/src/strategies/uint.rs create mode 100644 tooling/fuzzer/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 16988432243..cacfc06327d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2517,6 +2517,7 @@ dependencies = [ "nargo_fmt", "nargo_toml", "noir_debugger", + "noir_fuzzer", "noir_lsp", "noirc_abi", "noirc_artifacts", @@ -2529,6 +2530,7 @@ dependencies = [ "pprof 0.13.0", "predicates 2.1.5", "prettytable-rs", + "proptest", "rayon", "serde", "serde_json", @@ -2661,6 +2663,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "noir_fuzzer" +version = "0.31.0" +dependencies = [ + "acvm", + "nargo", + "noirc_abi", + "noirc_artifacts", + "proptest", + "rand 0.8.5", +] + [[package]] name = "noir_grumpkin" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 5fc9936de91..129661d157d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ # Crates related to tooling built on top of the Noir compiler "tooling/lsp", "tooling/debugger", + "tooling/fuzzer", "tooling/nargo", "tooling/nargo_fmt", "tooling/nargo_cli", @@ -69,6 +70,7 @@ noirc_frontend = { path = "compiler/noirc_frontend" } noirc_printable_type = { path = "compiler/noirc_printable_type" } # Noir tooling workspace dependencies +noir_fuzzer = { path = "tooling/fuzzer" } nargo = { path = "tooling/nargo" } nargo_fmt = { path = "tooling/nargo_fmt" } nargo_toml = { path = "tooling/nargo_toml" } diff --git a/compiler/noirc_frontend/src/elaborator/lints.rs b/compiler/noirc_frontend/src/elaborator/lints.rs index 4859ac5f97c..b86912940eb 100644 --- a/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/compiler/noirc_frontend/src/elaborator/lints.rs @@ -130,15 +130,6 @@ pub(super) fn recursive_non_entrypoint_function( } } -/// Test functions cannot have arguments in order to be executable. -pub(super) fn test_function_with_args(func: &NoirFunction) -> Option { - if func.attributes().is_test_function() && !func.parameters().is_empty() { - Some(ResolverError::TestFunctionHasParameters { span: func.name_ident().span() }) - } else { - None - } -} - /// Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime. pub(super) fn unconstrained_function_args( function_args: &[(Type, ExprId, Span)], diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 053de6e1735..91d6ba71927 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -581,7 +581,6 @@ impl<'context> Elaborator<'context> { self.run_lint(|elaborator| { lints::low_level_function_outside_stdlib(func, elaborator.crate_id).map(Into::into) }); - self.run_lint(|_| lints::test_function_with_args(func).map(Into::into)); self.run_lint(|_| { lints::recursive_non_entrypoint_function(func, is_entry_point).map(Into::into) }); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 1dcace82372..97de66be817 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -23,7 +23,7 @@ use crate::hir_def::expr::{ use crate::hir_def::function::FunctionBody; use crate::hir_def::traits::{Trait, TraitConstraint}; use crate::macros_api::SecondaryAttribute; -use crate::token::{Attributes, FunctionAttribute}; +use crate::token::Attributes; use regex::Regex; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::rc::Rc; @@ -1043,14 +1043,6 @@ impl<'a> Resolver<'a> { }); } - if matches!(attributes.function, Some(FunctionAttribute::Test { .. })) - && !parameters.is_empty() - { - self.push_err(ResolverError::TestFunctionHasParameters { - span: func.name_ident().span(), - }); - } - let mut typ = Type::Function(parameter_types, return_type, Box::new(Type::Unit)); if !generics.is_empty() { diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index 173fa54863a..829ab09ee1e 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -319,13 +319,12 @@ mod tests { use crate::uint128::{U128, pow64, pow63}; #[test] - fn test_not() { - let num = U128::from_u64s_le(0, 0); + fn test_not(lo: u64, hi: u64) { + let num = U128::from_u64s_le(lo, hi); let not_num = num.not(); - let max_u64: Field = pow64 - 1; - assert_eq(not_num.hi, max_u64); - assert_eq(not_num.lo, max_u64); + assert_eq(not_num.hi, (hi.not() as Field)); + assert_eq(not_num.lo, (lo.not() as Field)); let not_not_num = not_num.not(); assert_eq(num, not_not_num); @@ -493,6 +492,15 @@ mod tests { let end = a.to_integer(); assert_eq(start, end); } + + #[test] + fn integer_conversions_fuzz(lo: u64, hi: u64) { + let start: Field = (lo as Field) + pow64 * (hi as Field); + let a = U128::from_integer(start); + let end = a.to_integer(); + assert_eq(start, end); + } + #[test] fn test_wrapping_mul() { // 1*0==0 diff --git a/tooling/fuzzer/Cargo.toml b/tooling/fuzzer/Cargo.toml new file mode 100644 index 00000000000..ef49d707d6a --- /dev/null +++ b/tooling/fuzzer/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "noir_fuzzer" +description = "A fuzzer for Noir programs" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +acvm.workspace = true +nargo.workspace = true +noirc_artifacts.workspace = true +noirc_abi.workspace = true +proptest.workspace = true +rand.workspace = true diff --git a/tooling/fuzzer/src/lib.rs b/tooling/fuzzer/src/lib.rs new file mode 100644 index 00000000000..42dccc1dc83 --- /dev/null +++ b/tooling/fuzzer/src/lib.rs @@ -0,0 +1,93 @@ +//! This module has been adapted from Foundry's fuzzing implementation for the EVM. +//! https://github.com/foundry-rs/foundry/blob/6a85dbaa62f1c305f31cab37781232913055ae28/crates/evm/evm/src/executors/fuzz/mod.rs#L40 +//! +//! Code is used under the MIT license. + +use acvm::{blackbox_solver::StubbedBlackBoxSolver, FieldElement}; +use noirc_abi::InputMap; +use proptest::test_runner::{TestCaseError, TestError, TestRunner}; + +mod strategies; +mod types; + +use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome, FuzzTestResult}; + +use noirc_artifacts::program::ProgramArtifact; + +use nargo::ops::{execute_program, DefaultForeignCallExecutor}; + +/// An executor for Noir programs which which provides fuzzing support using [`proptest`]. +/// +/// After instantiation, calling `fuzz` will proceed to hammer the program with +/// inputs, until it finds a counterexample. The provided [`TestRunner`] contains all the +/// configuration which can be overridden via [environment variables](proptest::test_runner::Config) +pub struct FuzzedExecutor { + /// The program to be fuzzed + program: ProgramArtifact, + + /// The fuzzer + runner: TestRunner, +} + +impl FuzzedExecutor { + /// Instantiates a fuzzed executor given a testrunner + pub fn new(program: ProgramArtifact, runner: TestRunner) -> Self { + Self { program, runner } + } + + /// Fuzzes the provided program. + pub fn fuzz(&self) -> FuzzTestResult { + let strategy = strategies::arb_input_map(&self.program.abi); + + let run_result: Result<(), TestError> = + self.runner.clone().run(&strategy, |input_map| { + let fuzz_res = self.single_fuzz(input_map)?; + + match fuzz_res { + FuzzOutcome::Case(_) => Ok(()), + FuzzOutcome::CounterExample(CounterExampleOutcome { + exit_reason: status, + .. + }) => Err(TestCaseError::fail(status)), + } + }); + + match run_result { + Ok(()) => FuzzTestResult { success: true, reason: None, counterexample: None }, + + Err(TestError::Abort(reason)) => FuzzTestResult { + success: false, + reason: Some(reason.to_string()), + counterexample: None, + }, + Err(TestError::Fail(reason, counterexample)) => { + let reason = reason.to_string(); + let reason = if reason.is_empty() { None } else { Some(reason) }; + + FuzzTestResult { success: false, reason, counterexample: Some(counterexample) } + } + } + } + + /// Granular and single-step function that runs only one fuzz and returns either a `CaseOutcome` + /// or a `CounterExampleOutcome` + pub fn single_fuzz(&self, input_map: InputMap) -> Result { + let initial_witness = self.program.abi.encode(&input_map, None).unwrap(); + let result = execute_program( + &self.program.bytecode, + initial_witness, + &StubbedBlackBoxSolver, + &mut DefaultForeignCallExecutor::::new(false, None), + ); + + // TODO: Add handling for `vm.assume` equivalent + + match result { + Ok(_) => Ok(FuzzOutcome::Case(CaseOutcome { case: input_map })), + Err(err) => Ok(FuzzOutcome::CounterExample(CounterExampleOutcome { + exit_reason: err.to_string(), + counterexample: input_map, + })), + } + } +} diff --git a/tooling/fuzzer/src/strategies/int.rs b/tooling/fuzzer/src/strategies/int.rs new file mode 100644 index 00000000000..d11cafcfae5 --- /dev/null +++ b/tooling/fuzzer/src/strategies/int.rs @@ -0,0 +1,83 @@ +use proptest::{ + strategy::{NewTree, Strategy}, + test_runner::TestRunner, +}; +use rand::Rng; + +/// Strategy for signed ints (up to i128). +/// The strategy combines 2 different strategies, each assigned a specific weight: +/// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` +/// param). Then generate a value for this bit size. +/// 2. Generate a random value around the edges (+/- 3 around min, 0 and max possible value) +#[derive(Debug)] +pub struct IntStrategy { + /// Bit size of int (e.g. 128) + bits: usize, + /// The weight for edge cases (+/- 3 around 0 and max possible value) + edge_weight: usize, + /// The weight for purely random values + random_weight: usize, +} + +impl IntStrategy { + /// Create a new strategy. + /// # Arguments + /// * `bits` - Size of int in bits + pub fn new(bits: usize) -> Self { + Self { bits, edge_weight: 10usize, random_weight: 50usize } + } + + fn generate_edge_tree(&self, runner: &mut TestRunner) -> NewTree { + let rng = runner.rng(); + + let offset = rng.gen_range(0..4); + // Choose if we want values around min, -0, +0, or max + let kind = rng.gen_range(0..4); + let start = match kind { + 0 => self.type_min() + offset, + 1 => -offset - 1i128, + 2 => offset, + 3 => self.type_max() - offset, + _ => unreachable!(), + }; + Ok(proptest::num::i128::BinarySearch::new(start)) + } + + fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { + let rng = runner.rng(); + + let start: i128 = rng.gen_range(self.type_min()..=self.type_max()); + Ok(proptest::num::i128::BinarySearch::new(start)) + } + + fn type_max(&self) -> i128 { + if self.bits < 128 { + (1i128 << (self.bits - 1)) - 1 + } else { + i128::MAX + } + } + + fn type_min(&self) -> i128 { + if self.bits < 128 { + -(1i128 << (self.bits - 1)) + } else { + i128::MIN + } + } +} + +impl Strategy for IntStrategy { + type Tree = proptest::num::i128::BinarySearch; + type Value = i128; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + let total_weight = self.random_weight + self.edge_weight; + let bias = runner.rng().gen_range(0..total_weight); + // randomly select one of 2 strategies + match bias { + x if x < self.edge_weight => self.generate_edge_tree(runner), + _ => self.generate_random_tree(runner), + } + } +} diff --git a/tooling/fuzzer/src/strategies/mod.rs b/tooling/fuzzer/src/strategies/mod.rs new file mode 100644 index 00000000000..f5b03953ba8 --- /dev/null +++ b/tooling/fuzzer/src/strategies/mod.rs @@ -0,0 +1,97 @@ +use int::IntStrategy; +use prop::collection::vec; +use proptest::prelude::*; + +use acvm::{AcirField, FieldElement}; + +use noirc_abi::{input_parser::InputValue, Abi, AbiType, InputMap, Sign}; +use std::collections::BTreeMap; +use uint::UintStrategy; + +mod int; +mod uint; + +proptest::prop_compose! { + pub(super) fn arb_field_from_integer(bit_size: u32)(value: u128)-> FieldElement { + let width = (bit_size % 128).clamp(1, 127); + let max_value = 2u128.pow(width) - 1; + let value = value % max_value; + FieldElement::from(value) + } +} + +pub(super) fn arb_value_from_abi_type(abi_type: &AbiType) -> SBoxedStrategy { + match abi_type { + AbiType::Field => vec(any::(), 32) + .prop_map(|bytes| InputValue::Field(FieldElement::from_be_bytes_reduce(&bytes))) + .sboxed(), + AbiType::Integer { width, sign } if sign == &Sign::Unsigned => { + UintStrategy::new(*width as usize) + .prop_map(|uint| InputValue::Field(uint.into())) + .sboxed() + } + AbiType::Integer { width, .. } => { + let shift = 2i128.pow(*width); + IntStrategy::new(*width as usize) + .prop_map(move |mut int| { + if int < 0 { + int += shift + } + InputValue::Field(int.into()) + }) + .sboxed() + } + AbiType::Boolean => { + any::().prop_map(|val| InputValue::Field(FieldElement::from(val))).sboxed() + } + + AbiType::String { length } => { + // Strings only allow ASCII characters as each character must be able to be represented by a single byte. + let string_regex = format!("[[:ascii:]]{{{length}}}"); + proptest::string::string_regex(&string_regex) + .expect("parsing of regex should always succeed") + .prop_map(InputValue::String) + .sboxed() + } + AbiType::Array { length, typ } => { + let length = *length as usize; + let elements = vec(arb_value_from_abi_type(typ), length..=length); + + elements.prop_map(InputValue::Vec).sboxed() + } + + AbiType::Struct { fields, .. } => { + let fields: Vec> = fields + .iter() + .map(|(name, typ)| (Just(name.clone()), arb_value_from_abi_type(typ)).sboxed()) + .collect(); + + fields + .prop_map(|fields| { + let fields: BTreeMap<_, _> = fields.into_iter().collect(); + InputValue::Struct(fields) + }) + .sboxed() + } + + AbiType::Tuple { fields } => { + let fields: Vec<_> = fields.iter().map(arb_value_from_abi_type).collect(); + fields.prop_map(InputValue::Vec).sboxed() + } + } +} + +pub(super) fn arb_input_map(abi: &Abi) -> BoxedStrategy { + let values: Vec<_> = abi + .parameters + .iter() + .map(|param| (Just(param.name.clone()), arb_value_from_abi_type(¶m.typ))) + .collect(); + + values + .prop_map(|values| { + let input_map: InputMap = values.into_iter().collect(); + input_map + }) + .boxed() +} diff --git a/tooling/fuzzer/src/strategies/uint.rs b/tooling/fuzzer/src/strategies/uint.rs new file mode 100644 index 00000000000..5021e832b97 --- /dev/null +++ b/tooling/fuzzer/src/strategies/uint.rs @@ -0,0 +1,68 @@ +use proptest::{ + strategy::{NewTree, Strategy}, + test_runner::TestRunner, +}; +use rand::Rng; + +/// Value tree for unsigned ints (up to u128). +/// The strategy combines 2 different strategies, each assigned a specific weight: +/// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` +/// param). Then generate a value for this bit size. +/// 2. Generate a random value around the edges (+/- 3 around 0 and max possible value) +#[derive(Debug)] +pub struct UintStrategy { + /// Bit size of uint (e.g. 128) + bits: usize, + + /// The weight for edge cases (+/- 3 around 0 and max possible value) + edge_weight: usize, + /// The weight for purely random values + random_weight: usize, +} + +impl UintStrategy { + /// Create a new strategy. + /// # Arguments + /// * `bits` - Size of uint in bits + pub fn new(bits: usize) -> Self { + Self { bits, edge_weight: 10usize, random_weight: 50usize } + } + + fn generate_edge_tree(&self, runner: &mut TestRunner) -> NewTree { + let rng = runner.rng(); + // Choose if we want values around 0 or max + let is_min = rng.gen_bool(0.5); + let offset = rng.gen_range(0..4); + let start = if is_min { offset } else { self.type_max().saturating_sub(offset) }; + Ok(proptest::num::u128::BinarySearch::new(start)) + } + + fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { + let rng = runner.rng(); + let start = rng.gen_range(0..=self.type_max()); + + Ok(proptest::num::u128::BinarySearch::new(start)) + } + + fn type_max(&self) -> u128 { + if self.bits < 128 { + (1 << self.bits) - 1 + } else { + u128::MAX + } + } +} + +impl Strategy for UintStrategy { + type Tree = proptest::num::u128::BinarySearch; + type Value = u128; + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + let total_weight = self.random_weight + self.edge_weight; + let bias = runner.rng().gen_range(0..total_weight); + // randomly select one of 2 strategies + match bias { + x if x < self.edge_weight => self.generate_edge_tree(runner), + _ => self.generate_random_tree(runner), + } + } +} diff --git a/tooling/fuzzer/src/types.rs b/tooling/fuzzer/src/types.rs new file mode 100644 index 00000000000..dbd4ba94ec1 --- /dev/null +++ b/tooling/fuzzer/src/types.rs @@ -0,0 +1,42 @@ +use noirc_abi::InputMap; + +type CounterExample = InputMap; + +/// The outcome of a fuzz test +#[derive(Debug)] +pub struct FuzzTestResult { + /// Whether the test case was successful. This means that the program executed + /// properly, or that there was a constraint failure and that the test was expected to fail + /// (has the `should_fail` attribute) + pub success: bool, + + /// If there was a constraint failure, this field will be populated. Note that the test can + /// still be successful (i.e self.success == true) when it's expected to fail. + pub reason: Option, + + /// Minimal reproduction test case for failing fuzz tests + pub counterexample: Option, +} + +/// Returned by a single fuzz in the case of a successful run +#[derive(Debug)] +pub struct CaseOutcome { + /// Data of a single fuzz test case + pub case: InputMap, +} + +/// Returned by a single fuzz when a counterexample has been discovered +#[derive(Debug)] +pub struct CounterExampleOutcome { + /// Minimal reproduction test case for failing test + pub counterexample: CounterExample, + /// The status of the call + pub exit_reason: String, +} + +/// Outcome of a single fuzz +#[derive(Debug)] +pub enum FuzzOutcome { + Case(CaseOutcome), + CounterExample(CounterExampleOutcome), +} diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index c3beafb7db5..b9d7d7e3e48 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -32,6 +32,7 @@ noirc_driver.workspace = true noirc_frontend = { workspace = true, features = ["bn254"] } noirc_abi.workspace = true noirc_errors.workspace = true +noir_fuzzer.workspace = true noirc_artifacts.workspace = true acvm = { workspace = true, features = ["bn254"] } bn254_blackbox_solver.workspace = true @@ -50,6 +51,7 @@ color-eyre = "0.6.2" tokio = { version = "1.0", features = ["io-std", "rt"] } dap.workspace = true clap-markdown = { git = "https://github.com/noir-lang/clap-markdown", rev = "450d759532c88f0dba70891ceecdbc9ff8f25d2b", optional = true } +proptest.workspace = true notify = "6.1.1" notify-debouncer-full = "0.3.1" diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 1cda8d8382e..de9e8dc5d7c 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -10,7 +10,8 @@ use nargo::{ }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ - check_crate, file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, + check_crate, compile_no_check, file_manager_with_stdlib, CompileOptions, + NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::{ graph::CrateName, @@ -185,14 +186,47 @@ fn run_test + Default>( let blackbox_solver = S::default(); - nargo::ops::run_test( - &blackbox_solver, - &mut context, - test_function, - show_output, - foreign_call_resolver_url, - compile_options, - ) + let test_function_has_no_arguments = context + .def_interner + .function_meta(&test_function.get_id()) + .function_signature() + .0 + .is_empty(); + + if test_function_has_no_arguments { + nargo::ops::run_test( + &blackbox_solver, + &mut context, + test_function, + show_output, + foreign_call_resolver_url, + compile_options, + ) + } else { + use noir_fuzzer::FuzzedExecutor; + use proptest::test_runner::TestRunner; + + let compiled_program = + compile_no_check(&mut context, compile_options, test_function.get_id(), None, false); + match compiled_program { + Ok(compiled_program) => { + let runner = TestRunner::default(); + + let fuzzer = FuzzedExecutor::new(compiled_program.into(), runner); + + let result = fuzzer.fuzz(); + if result.success { + TestStatus::Pass + } else { + TestStatus::Fail { + message: result.reason.unwrap_or_default(), + error_diagnostic: None, + } + } + } + Err(err) => TestStatus::CompileError(err.into()), + } + } } fn get_tests_in_package( diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs index 70857b4b65e..5badd76605a 100644 --- a/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -3,7 +3,7 @@ use std::{collections::BTreeMap, path::PathBuf}; use acvm::blackbox_solver::StubbedBlackBoxSolver; use fm::FileManager; -use noirc_driver::{check_crate, file_manager_with_stdlib, CompileOptions}; +use noirc_driver::{check_crate, compile_no_check, file_manager_with_stdlib, CompileOptions}; use noirc_frontend::hir::FunctionNameMatch; use nargo::{ @@ -47,15 +47,52 @@ fn run_stdlib_tests() { let test_report: Vec<(String, TestStatus)> = test_functions .into_iter() .map(|(test_name, test_function)| { - let status = run_test( - &StubbedBlackBoxSolver, - &mut context, - &test_function, - false, - None, - &CompileOptions::default(), - ); - + let test_function_has_no_arguments = context + .def_interner + .function_meta(&test_function.get_id()) + .function_signature() + .0 + .is_empty(); + + let status = if test_function_has_no_arguments { + run_test( + &StubbedBlackBoxSolver, + &mut context, + &test_function, + false, + None, + &CompileOptions::default(), + ) + } else { + use noir_fuzzer::FuzzedExecutor; + use proptest::test_runner::TestRunner; + + let compiled_program = compile_no_check( + &mut context, + &CompileOptions::default(), + test_function.get_id(), + None, + false, + ); + match compiled_program { + Ok(compiled_program) => { + let runner = TestRunner::default(); + + let fuzzer = FuzzedExecutor::new(compiled_program.into(), runner); + + let result = fuzzer.fuzz(); + if result.success { + TestStatus::Pass + } else { + TestStatus::Fail { + message: result.reason.unwrap_or_default(), + error_diagnostic: None, + } + } + } + Err(err) => TestStatus::CompileError(err.into()), + } + }; (test_name, status) }) .collect(); diff --git a/tooling/noirc_abi/Cargo.toml b/tooling/noirc_abi/Cargo.toml index e8f57060367..4c0c1f75e42 100644 --- a/tooling/noirc_abi/Cargo.toml +++ b/tooling/noirc_abi/Cargo.toml @@ -25,7 +25,6 @@ strum_macros = "0.24" proptest.workspace = true proptest-derive.workspace = true - [features] bn254 = ["acvm/bn254"] -bls12_381 = ["acvm/bls12_381"] \ No newline at end of file +bls12_381 = ["acvm/bls12_381"] From 3ef36458fef36b2a2f6cf99b35a43339f3721b27 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:29:38 +0200 Subject: [PATCH 48/57] fix: avoid duplicating constant arrays (#5287) # Description ## Problem\* Resolves #5286 ## Summary\* Keep track of constant arrays and the array they resolve to in mem2reg, so that another constant array is not created for the same array. ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/ir/function_inserter.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index 68ece87c7c7..a063a7ff268 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -16,11 +16,12 @@ pub(crate) struct FunctionInserter<'f> { pub(crate) function: &'f mut Function, values: HashMap, + const_arrays: HashMap, ValueId>, } impl<'f> FunctionInserter<'f> { pub(crate) fn new(function: &'f mut Function) -> FunctionInserter<'f> { - Self { function, values: HashMap::default() } + Self { function, values: HashMap::default(), const_arrays: HashMap::default() } } /// Resolves a ValueId to its new, updated value. @@ -34,10 +35,17 @@ impl<'f> FunctionInserter<'f> { super::value::Value::Array { array, typ } => { let array = array.clone(); let typ = typ.clone(); - let new_array = array.iter().map(|id| self.resolve(*id)).collect(); - let new_id = self.function.dfg.make_array(new_array, typ); - self.values.insert(value, new_id); - new_id + let new_array: im::Vector = + array.iter().map(|id| self.resolve(*id)).collect(); + if self.const_arrays.get(&new_array) == Some(&value) { + value + } else { + let new_array_clone = new_array.clone(); + let new_id = self.function.dfg.make_array(new_array, typ); + self.values.insert(value, new_id); + self.const_arrays.insert(new_array_clone, new_id); + new_id + } } _ => value, }, From 19884f161dfc7d7ce75dd2c404b8ef39cdad2240 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:41:43 +0100 Subject: [PATCH 49/57] fix: avoid unnecessarily splitting expressions with multiplication terms with a shared term (#5291) # Description ## Problem\* Resolves ## Summary\* This PR addresses an issue where we were unnecessarily splitting an expression based on an example which Zac found while working on `noir-edwards`. We were being overly restrictive and only accepting the case where both witnesses in the multiplication are shared with other terms ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../acvm/src/compiler/transformers/csat.rs | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/acvm-repo/acvm/src/compiler/transformers/csat.rs b/acvm-repo/acvm/src/compiler/transformers/csat.rs index f2a3cc2c84e..19cc18ca7f3 100644 --- a/acvm-repo/acvm/src/compiler/transformers/csat.rs +++ b/acvm-repo/acvm/src/compiler/transformers/csat.rs @@ -432,7 +432,7 @@ fn fits_in_one_identity(expr: &Expression, width: usize) -> boo return true; } - // We now know that we have a single mul term. We also know that the mul term must match up with two other terms + // We now know that we have a single mul term. We also know that the mul term must match up with at least one of the other terms // A polynomial whose mul terms are non zero which do not match up with two terms in the fan-in cannot fit into one opcode // An example of this is: Axy + Bx + Cy + ... // Notice how the bivariate monomial xy has two univariate monomials with their respective coefficients @@ -461,7 +461,25 @@ fn fits_in_one_identity(expr: &Expression, width: usize) -> boo } } - found_x & found_y + // If the multiplication is a squaring then we must assign the two witnesses to separate wires and so we + // can never get a zero contribution to the width. + let multiplication_is_squaring = mul_term.1 == mul_term.2; + + let mul_term_width_contribution = if !multiplication_is_squaring && (found_x & found_y) { + // Both witnesses involved in the multiplication exist elsewhere in the expression. + // They both do not contribute to the width of the expression as this would be double-counting + // due to their appearance in the linear terms. + 0 + } else if found_x || found_y { + // One of the witnesses involved in the multiplication exists elsewhere in the expression. + // The multiplication then only contributes 1 new witness to the width. + 1 + } else { + // Worst case scenario, the multiplication is using completely unique witnesses so has a contribution of 2. + 2 + }; + + mul_term_width_contribution + expr.linear_combinations.len() <= width } #[cfg(test)] @@ -573,4 +591,20 @@ mod tests { let contains_b = got_optimized_opcode_a.linear_combinations.iter().any(|(_, w)| *w == b); assert!(contains_b); } + + #[test] + fn recognize_expr_with_single_shared_witness_which_fits_in_single_identity() { + // Regression test for an expression which Zac found which should have been preserved but + // was being split into two expressions. + let expr = Expression { + mul_terms: vec![(-FieldElement::from(555u128), Witness(8), Witness(10))], + linear_combinations: vec![ + (FieldElement::one(), Witness(10)), + (FieldElement::one(), Witness(11)), + (-FieldElement::one(), Witness(13)), + ], + q_c: FieldElement::zero(), + }; + assert!(fits_in_one_identity(&expr, 4)); + } } From 0ea608f10bdeb26df7dfc17b1a0bad5db1967be8 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 19 Jun 2024 10:48:40 -0500 Subject: [PATCH 50/57] fix: Don't lazily elaborate functions (#5282) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5271 ## Summary\* Lazily elaborating functions wasn't correct since the FuncMeta object never stored the LocalModuleId carried by the set of unresolved functions. This lead to lazily elaborated functions be elaborated in a different module than intended. ## Additional Context Lazily elaborated functions seem to no longer be necessary to run our tests, nor to run the tests in aztec-nr so I've just removed them rather than adding the additional fields to FuncMeta. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/elaborator/patterns.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index e337726b579..4d07009e064 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -11,11 +11,10 @@ use crate::{ }, hir_def::{ expr::{HirIdent, ImplKind}, - function::FunctionBody, stmt::HirPattern, }, macros_api::{HirExpression, Ident, Path, Pattern}, - node_interner::{DefinitionId, DefinitionKind, DependencyId, ExprId, GlobalId, TraitImplKind}, + node_interner::{DefinitionId, DefinitionKind, ExprId, GlobalId, TraitImplKind}, Shared, StructType, Type, TypeBindings, }; @@ -415,16 +414,6 @@ impl<'context> Elaborator<'context> { match self.interner.definition(hir_ident.id).kind { DefinitionKind::Function(id) => { if let Some(current_item) = self.current_item { - // Lazily evaluate functions found within globals if necessary. - // Otherwise if we later attempt to evaluate the global it will - // see an empty function body. - if matches!(current_item, DependencyId::Global(_)) { - let meta = self.interner.function_meta(&id); - - if matches!(&meta.function_body, FunctionBody::Unresolved(..)) { - self.elaborate_function(id); - } - } self.interner.add_function_dependency(current_item, id); } } From d2ea8a9a994417b82fefb5ef89eef466737d039b Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Wed, 19 Jun 2024 18:12:06 +0200 Subject: [PATCH 51/57] chore: pedersen hash in Noir (#5217) # Description ## Problem\* Related to #4931 ## Summary\* Noir implementation for Pedersen Hash. ## Additional Context I added a derive_generators() built-in function, which only works for bn254, and then use pedersen commitments and scalar mul. ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 1 + .../src/generator/generators.rs | 2 +- acvm-repo/bn254_blackbox_solver/src/lib.rs | 13 +++-- compiler/noirc_evaluator/Cargo.toml | 1 + .../noirc_evaluator/src/ssa/ir/instruction.rs | 6 ++- .../src/ssa/ir/instruction/call.rs | 52 +++++++++++++++++++ .../src/ssa/opt/remove_enable_side_effects.rs | 3 +- .../src/ssa/opt/remove_if_else.rs | 3 +- noir_stdlib/src/hash.nr | 30 +++++++++-- 9 files changed, 98 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cacfc06327d..eb058008359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2836,6 +2836,7 @@ name = "noirc_evaluator" version = "0.31.0" dependencies = [ "acvm", + "bn254_blackbox_solver", "chrono", "fxhash", "im", diff --git a/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index f89d582d167..bb51426b33b 100644 --- a/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -38,7 +38,7 @@ fn default_generators() -> &'static [Affine; NUM_DEFAULT_GEN /// index-addressable generators. /// /// [hash_to_curve]: super::hash_to_curve::hash_to_curve -pub(crate) fn derive_generators( +pub fn derive_generators( domain_separator_bytes: &[u8], num_generators: u32, starting_index: u32, diff --git a/acvm-repo/bn254_blackbox_solver/src/lib.rs b/acvm-repo/bn254_blackbox_solver/src/lib.rs index 08e0fb66a6d..6897116e90e 100644 --- a/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -12,6 +12,7 @@ mod schnorr; use ark_ec::AffineRepr; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; +pub use generator::generators::derive_generators; pub use poseidon2::poseidon2_permutation; // Temporary hack, this ensure that we always use a bn254 field here @@ -47,11 +48,13 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { let inputs: Vec = inputs.iter().map(|input| input.into_repr()).collect(); let result = pedersen::commitment::commit_native_with_index(&inputs, domain_separator); - let res_x = - FieldElement::from_repr(*result.x().expect("should not commit to point at infinity")); - let res_y = - FieldElement::from_repr(*result.y().expect("should not commit to point at infinity")); - Ok((res_x, res_y)) + let result = if let Some((x, y)) = result.xy() { + (FieldElement::from_repr(*x), FieldElement::from_repr(*y)) + } else { + (FieldElement::from(0_u128), FieldElement::from(0_u128)) + }; + + Ok(result) } fn pedersen_hash( diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index aa30eef9156..72a52b43741 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -12,6 +12,7 @@ license.workspace = true noirc_frontend.workspace = true noirc_errors.workspace = true acvm.workspace = true +bn254_blackbox_solver.workspace = true fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index e21deb9ef79..8f881b86e47 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -67,6 +67,7 @@ pub(crate) enum Intrinsic { AsField, AsWitness, IsUnconstrained, + DerivePedersenGenerators, } impl std::fmt::Display for Intrinsic { @@ -92,6 +93,7 @@ impl std::fmt::Display for Intrinsic { Intrinsic::AsField => write!(f, "as_field"), Intrinsic::AsWitness => write!(f, "as_witness"), Intrinsic::IsUnconstrained => write!(f, "is_unconstrained"), + Intrinsic::DerivePedersenGenerators => write!(f, "derive_pedersen_generators"), } } } @@ -120,7 +122,8 @@ impl Intrinsic { | Intrinsic::StrAsBytes | Intrinsic::FromField | Intrinsic::AsField - | Intrinsic::IsUnconstrained => false, + | Intrinsic::IsUnconstrained + | Intrinsic::DerivePedersenGenerators => false, // Some black box functions have side-effects Intrinsic::BlackBox(func) => matches!( @@ -155,6 +158,7 @@ impl Intrinsic { "as_field" => Some(Intrinsic::AsField), "as_witness" => Some(Intrinsic::AsWitness), "is_unconstrained" => Some(Intrinsic::IsUnconstrained), + "derive_pedersen_generators" => Some(Intrinsic::DerivePedersenGenerators), other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox), } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 74e5653c7ba..dfb8d0a8bf9 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -2,6 +2,7 @@ use fxhash::FxHashMap as HashMap; use std::{collections::VecDeque, rc::Rc}; use acvm::{acir::AcirField, acir::BlackBoxFunc, BlackBoxResolutionError, FieldElement}; +use bn254_blackbox_solver::derive_generators; use iter_extended::vecmap; use num_bigint::BigUint; @@ -295,6 +296,13 @@ pub(super) fn simplify_call( } Intrinsic::AsWitness => SimplifyResult::None, Intrinsic::IsUnconstrained => SimplifyResult::None, + Intrinsic::DerivePedersenGenerators => { + if let Some(Type::Array(_, len)) = ctrl_typevars.unwrap().first() { + simplify_derive_generators(dfg, arguments, *len as u32) + } else { + unreachable!("Derive Pedersen Generators must return an array"); + } + } } } @@ -626,3 +634,47 @@ fn simplify_signature( _ => SimplifyResult::None, } } + +fn simplify_derive_generators( + dfg: &mut DataFlowGraph, + arguments: &[ValueId], + num_generators: u32, +) -> SimplifyResult { + if arguments.len() == 2 { + let domain_separator_string = dfg.get_array_constant(arguments[0]); + let starting_index = dfg.get_numeric_constant(arguments[1]); + if let (Some(domain_separator_string), Some(starting_index)) = + (domain_separator_string, starting_index) + { + let domain_separator_bytes = domain_separator_string + .0 + .iter() + .map(|&x| dfg.get_numeric_constant(x).unwrap().to_u128() as u8) + .collect::>(); + let generators = derive_generators( + &domain_separator_bytes, + num_generators, + starting_index.try_to_u32().expect("argument is declared as u32"), + ); + let is_infinite = dfg.make_constant(FieldElement::zero(), Type::bool()); + let mut results = Vec::new(); + for gen in generators { + let x_big: BigUint = gen.x.into(); + let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); + let y_big: BigUint = gen.y.into(); + let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); + results.push(dfg.make_constant(x, Type::field())); + results.push(dfg.make_constant(y, Type::field())); + results.push(is_infinite); + } + let len = results.len(); + let result = + dfg.make_array(results.into(), Type::Array(vec![Type::field()].into(), len)); + SimplifyResult::SimplifiedTo(result) + } else { + SimplifyResult::None + } + } else { + unreachable!("Unexpected number of arguments to derive_generators"); + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index 6db76996747..c9a6b7bf9c3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -159,7 +159,8 @@ impl Context { | Intrinsic::AsField | Intrinsic::AsSlice | Intrinsic::AsWitness - | Intrinsic::IsUnconstrained => false, + | Intrinsic::IsUnconstrained + | Intrinsic::DerivePedersenGenerators => false, }, // We must assume that functions contain a side effect as we cannot inspect more deeply. diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 6ca7eb74e9d..fd7a1a06fc8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -233,6 +233,7 @@ fn slice_capacity_change( | Intrinsic::FromField | Intrinsic::AsField | Intrinsic::AsWitness - | Intrinsic::IsUnconstrained => SizeChange::None, + | Intrinsic::IsUnconstrained + | Intrinsic::DerivePedersenGenerators => SizeChange::None, } } diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index 6c295d127ab..270de210815 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -5,7 +5,7 @@ mod poseidon2; use crate::default::Default; use crate::uint128::U128; use crate::sha256::{digest, sha256_var}; -use crate::embedded_curve_ops::EmbeddedCurvePoint; +use crate::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar}; #[foreign(sha256)] // docs:start:sha256 @@ -28,7 +28,12 @@ pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:start:pedersen_commitment pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { // docs:end:pedersen_commitment - pedersen_commitment_with_separator(input, 0) + let value = pedersen_commitment_with_separator(input, 0); + if (value.x == 0) & (value.y == 0) { + EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true } + } else { + EmbeddedCurvePoint { x: value.x, y: value.y, is_infinite: false } + } } #[foreign(pedersen_commitment)] @@ -46,8 +51,25 @@ pub fn pedersen_hash(input: [Field; N]) -> Field pedersen_hash_with_separator(input, 0) } -#[foreign(pedersen_hash)] -pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} +#[field(bn254)] +fn derive_generators(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] { + crate::assert_constant(domain_separator_bytes); + crate::assert_constant(starting_index); + __derive_generators(domain_separator_bytes, starting_index) +} + +#[builtin(derive_pedersen_generators)] +#[field(bn254)] +fn __derive_generators(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] {} + +pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field { + let v1 = pedersen_commitment(input); + let length_generator :[EmbeddedCurvePoint;1] = derive_generators("pedersen_hash_length".as_bytes(), separator); + crate::embedded_curve_ops::multi_scalar_mul( + [length_generator[0], v1], + [EmbeddedCurveScalar { lo: N as Field, hi: 0 }, EmbeddedCurveScalar { lo: 1, hi: 0 }] + )[0] +} pub fn hash_to_field(inputs: [Field]) -> Field { let mut sum = 0; From 1a794e312159d54a2bf21ae5f61a3de6fa688127 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Wed, 19 Jun 2024 19:42:09 +0200 Subject: [PATCH 52/57] chore: Pedersen commitment in Noir (#5221) # Description ## Problem\* Related to #4931 ## Summary\* Implements Pedersen commitment in Noir and remove Pedersen Commitment, Pedersen Hash blackboxes. ## Additional Context The PR is in draft until #5217 is merged. ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: TomAFrench --- .../acir/src/circuit/black_box_functions.rs | 45 ++--------- .../opcodes/black_box_function_call.rs | 30 ++++--- .../acir/tests/test_program_serialization.rs | 27 ------- acvm-repo/acvm/src/pwg/blackbox/mod.rs | 12 +-- acvm-repo/acvm/src/pwg/blackbox/pedersen.rs | 47 ----------- .../test/browser/execute_circuit.test.ts | 10 --- .../acvm_js/test/node/execute_circuit.test.ts | 30 ------- acvm-repo/acvm_js/test/shared/pedersen.ts | 13 --- .../src/curve_specific_solver.rs | 24 ------ .../benches/criterion.rs | 18 +---- acvm-repo/bn254_blackbox_solver/src/lib.rs | 28 ------- acvm-repo/brillig/src/black_box.rs | 4 +- acvm-repo/brillig_vm/src/black_box.rs | 41 +--------- .../brillig/brillig_gen/brillig_black_box.rs | 35 +------- .../noirc_evaluator/src/brillig/brillig_ir.rs | 15 +--- .../src/brillig/brillig_ir/debug_show.rs | 20 +---- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 25 ------ .../ssa/acir_gen/acir_ir/generated_acir.rs | 25 ++---- .../src/ssa/ir/instruction/call.rs | 4 +- .../src/ssa/opt/flatten_cfg.rs | 79 ++++++++++--------- noir_stdlib/src/embedded_curve_ops.nr | 8 ++ noir_stdlib/src/hash.nr | 16 ++-- .../intrinsic_die/src/main.nr | 1 - tooling/lsp/src/solver.rs | 16 ---- 24 files changed, 104 insertions(+), 469 deletions(-) delete mode 100644 acvm-repo/acvm/src/pwg/blackbox/pedersen.rs delete mode 100644 acvm-repo/acvm_js/test/shared/pedersen.ts diff --git a/acvm-repo/acir/src/circuit/black_box_functions.rs b/acvm-repo/acir/src/circuit/black_box_functions.rs index 419c0266b69..aadee59f507 100644 --- a/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -82,43 +82,10 @@ pub enum BlackBoxFunc { /// /// [grumpkin]: https://hackmd.io/@aztec-network/ByzgNxBfd#2-Grumpkin---A-curve-on-top-of-BN-254-for-SNARK-efficient-group-operations SchnorrVerify, - - /// Calculates a Pedersen commitment to the inputs. - /// - /// Computes a Pedersen commitment of the inputs using generators of the - /// embedded curve - /// - input: vector of (witness, 254) - /// - output: 2 witnesses representing the x,y coordinates of the resulting - /// Grumpkin point - /// - domain separator: a constant public value (a field element) that you - /// can use so that the commitment also depends on the domain separator. - /// Noir uses 0 as domain separator. - /// - /// The backend should handle proper conversion between the inputs being ACIR - /// field elements and the scalar field of the embedded curve. In the case of - /// Aztec's Barretenberg, the latter is bigger than the ACIR field so it is - /// straightforward. The Pedersen generators are managed by the proving - /// system. - /// - /// The commitment is expected to be additively homomorphic + /// Deprecated. To be removed with a sync from aztec-packages PedersenCommitment, - - /// Calculates a Pedersen hash to the inputs. - /// - /// Computes a Pedersen hash of the inputs and their number, using - /// generators of the embedded curve - /// - input: vector of (witness, 254) - /// - output: the x-coordinate of the pedersen commitment of the - /// 'prepended input' (see below) - /// - domain separator: a constant public value (a field element) that you - /// can use so that the hash also depends on the domain separator. Noir - /// uses 0 as domain separator. - /// - /// In Barretenberg, PedersenHash is doing the same as PedersenCommitment, - /// except that it prepends the inputs with their length. This is expected - /// to not be additively homomorphic. + /// Deprecated. To be removed with a sync from aztec-packages PedersenHash, - /// Verifies a ECDSA signature over the secp256k1 curve. /// - inputs: /// - x coordinate of public key as 32 bytes @@ -242,8 +209,6 @@ impl BlackBoxFunc { BlackBoxFunc::SchnorrVerify => "schnorr_verify", BlackBoxFunc::Blake2s => "blake2s", BlackBoxFunc::Blake3 => "blake3", - BlackBoxFunc::PedersenCommitment => "pedersen_commitment", - BlackBoxFunc::PedersenHash => "pedersen_hash", BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1", BlackBoxFunc::MultiScalarMul => "multi_scalar_mul", BlackBoxFunc::EmbeddedCurveAdd => "embedded_curve_add", @@ -262,6 +227,8 @@ impl BlackBoxFunc { BlackBoxFunc::BigIntToLeBytes => "bigint_to_le_bytes", BlackBoxFunc::Poseidon2Permutation => "poseidon2_permutation", BlackBoxFunc::Sha256Compression => "sha256_compression", + BlackBoxFunc::PedersenCommitment => "deprecated pedersen commitment", + BlackBoxFunc::PedersenHash => "deprecated pedersen hash", } } @@ -272,8 +239,6 @@ impl BlackBoxFunc { "schnorr_verify" => Some(BlackBoxFunc::SchnorrVerify), "blake2s" => Some(BlackBoxFunc::Blake2s), "blake3" => Some(BlackBoxFunc::Blake3), - "pedersen_commitment" => Some(BlackBoxFunc::PedersenCommitment), - "pedersen_hash" => Some(BlackBoxFunc::PedersenHash), "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1), "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1), "multi_scalar_mul" => Some(BlackBoxFunc::MultiScalarMul), @@ -292,6 +257,8 @@ impl BlackBoxFunc { "bigint_to_le_bytes" => Some(BlackBoxFunc::BigIntToLeBytes), "poseidon2_permutation" => Some(BlackBoxFunc::Poseidon2Permutation), "sha256_compression" => Some(BlackBoxFunc::Sha256Compression), + "deprecated pedersen commitment" => Some(BlackBoxFunc::PedersenCommitment), + "deprecated pedersen hash" => Some(BlackBoxFunc::PedersenHash), _ => None, } } diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 362e9ba5936..09b39964813 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -54,11 +54,13 @@ pub enum BlackBoxFuncCall { message: Vec, output: Witness, }, + /// Deprecated. To be removed with a sync from aztec-packages PedersenCommitment { inputs: Vec, domain_separator: u32, outputs: (Witness, Witness), }, + /// Deprecated. To be removed with a sync from aztec-packages PedersenHash { inputs: Vec, domain_separator: u32, @@ -189,8 +191,6 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::Blake2s { .. } => BlackBoxFunc::Blake2s, BlackBoxFuncCall::Blake3 { .. } => BlackBoxFunc::Blake3, BlackBoxFuncCall::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify, - BlackBoxFuncCall::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, - BlackBoxFuncCall::PedersenHash { .. } => BlackBoxFunc::PedersenHash, BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, BlackBoxFuncCall::MultiScalarMul { .. } => BlackBoxFunc::MultiScalarMul, @@ -206,6 +206,8 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes, BlackBoxFuncCall::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation, BlackBoxFuncCall::Sha256Compression { .. } => BlackBoxFunc::Sha256Compression, + BlackBoxFuncCall::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, + BlackBoxFuncCall::PedersenHash { .. } => BlackBoxFunc::PedersenHash, } } @@ -219,8 +221,6 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Blake3 { inputs, .. } - | BlackBoxFuncCall::PedersenCommitment { inputs, .. } - | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(), @@ -318,6 +318,8 @@ impl BlackBoxFuncCall { inputs.push(*key_hash); inputs } + BlackBoxFuncCall::PedersenCommitment { .. } => todo!(), + BlackBoxFuncCall::PedersenHash { .. } => todo!(), } } @@ -339,9 +341,7 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::XOR { output, .. } | BlackBoxFuncCall::SchnorrVerify { output, .. } | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } - | BlackBoxFuncCall::PedersenHash { output, .. } | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output], - BlackBoxFuncCall::PedersenCommitment { outputs, .. } => vec![outputs.0, outputs.1], BlackBoxFuncCall::MultiScalarMul { outputs, .. } | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } => { vec![outputs.0, outputs.1, outputs.2] @@ -356,6 +356,8 @@ impl BlackBoxFuncCall { vec![] } BlackBoxFuncCall::BigIntToLeBytes { outputs, .. } => outputs.to_vec(), + BlackBoxFuncCall::PedersenCommitment { .. } => todo!(), + BlackBoxFuncCall::PedersenHash { .. } => todo!(), } } } @@ -421,6 +423,14 @@ fn get_outputs_string(outputs: &[Witness]) -> String { impl std::fmt::Display for BlackBoxFuncCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlackBoxFuncCall::PedersenCommitment { .. } => { + return write!(f, "BLACKBOX::Deprecated") + } + BlackBoxFuncCall::PedersenHash { .. } => return write!(f, "BLACKBOX::Deprecated"), + _ => (), + } + let uppercase_name = self.name().to_uppercase(); write!(f, "BLACKBOX::{uppercase_name} ")?; // INPUTS @@ -440,13 +450,7 @@ impl std::fmt::Display for BlackBoxFuncCall { write!(f, "]")?; - // SPECIFIC PARAMETERS - match self { - BlackBoxFuncCall::PedersenCommitment { domain_separator, .. } => { - write!(f, " domain_separator: {domain_separator}") - } - _ => write!(f, ""), - } + write!(f, "") } } diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index dfcb1a8bb86..84a9aa719f2 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -100,33 +100,6 @@ fn multi_scalar_mul_circuit() { assert_eq!(bytes, expected_serialization) } -#[test] -fn pedersen_circuit() { - let pedersen = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::PedersenCommitment { - inputs: vec![FunctionInput { witness: Witness(1), num_bits: FieldElement::max_num_bits() }], - outputs: (Witness(2), Witness(3)), - domain_separator: 0, - }); - - let circuit: Circuit = Circuit { - current_witness_index: 4, - opcodes: vec![pedersen], - private_parameters: BTreeSet::from([Witness(1)]), - return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(3)])), - ..Circuit::default() - }; - let program = Program { functions: vec![circuit], unconstrained_functions: vec![] }; - - let bytes = Program::serialize_program(&program); - - let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 74, 73, 10, 0, 0, 4, 180, 29, 252, 255, 193, 66, 40, - 76, 77, 179, 34, 20, 36, 136, 237, 83, 245, 101, 107, 79, 65, 94, 253, 214, 217, 255, 239, - 192, 1, 43, 124, 181, 238, 113, 0, 0, 0, - ]; - assert_eq!(bytes, expected_serialization) -} - #[test] fn schnorr_verify_circuit() { let public_key_x = diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 8bda9221d8a..b3064c47d82 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -7,7 +7,7 @@ use acvm_blackbox_solver::{blake2s, blake3, keccak256, keccakf1600, sha256}; use self::{ aes128::solve_aes128_encryption_opcode, bigint::AcvmBigIntSolver, - hash::solve_poseidon2_permutation_opcode, pedersen::pedersen_hash, + hash::solve_poseidon2_permutation_opcode, }; use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; @@ -18,7 +18,6 @@ pub(crate) mod bigint; mod embedded_curve_ops; mod hash; mod logic; -mod pedersen; mod range; mod signature; pub(crate) mod utils; @@ -27,7 +26,6 @@ use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; // Hash functions should eventually be exposed for external consumers. use hash::{solve_generic_256_hash_opcode, solve_sha_256_permutation_opcode}; use logic::{and, xor}; -use pedersen::pedersen; pub(crate) use range::solve_range_opcode; use signature::{ ecdsa::{secp256k1_prehashed, secp256r1_prehashed}, @@ -127,12 +125,6 @@ pub(crate) fn solve( message, *output, ), - BlackBoxFuncCall::PedersenCommitment { inputs, domain_separator, outputs } => { - pedersen(backend, initial_witness, inputs, *domain_separator, *outputs) - } - BlackBoxFuncCall::PedersenHash { inputs, domain_separator, output } => { - pedersen_hash(backend, initial_witness, inputs, *domain_separator, *output) - } BlackBoxFuncCall::EcdsaSecp256k1 { public_key_x, public_key_y, @@ -187,5 +179,7 @@ pub(crate) fn solve( BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len } => { solve_poseidon2_permutation_opcode(backend, initial_witness, inputs, outputs, *len) } + BlackBoxFuncCall::PedersenCommitment { .. } => todo!("Deprecated BlackBox"), + BlackBoxFuncCall::PedersenHash { .. } => todo!("Deprecated BlackBox"), } } diff --git a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs deleted file mode 100644 index f64a3a79465..00000000000 --- a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs +++ /dev/null @@ -1,47 +0,0 @@ -use acir::{ - circuit::opcodes::FunctionInput, - native_types::{Witness, WitnessMap}, - AcirField, -}; - -use crate::{ - pwg::{insert_value, witness_to_value, OpcodeResolutionError}, - BlackBoxFunctionSolver, -}; - -pub(super) fn pedersen( - backend: &impl BlackBoxFunctionSolver, - initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], - domain_separator: u32, - outputs: (Witness, Witness), -) -> Result<(), OpcodeResolutionError> { - let scalars: Result, _> = - inputs.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); - let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); - - let (res_x, res_y) = backend.pedersen_commitment(&scalars, domain_separator)?; - - insert_value(&outputs.0, res_x, initial_witness)?; - insert_value(&outputs.1, res_y, initial_witness)?; - - Ok(()) -} - -pub(super) fn pedersen_hash( - backend: &impl BlackBoxFunctionSolver, - initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], - domain_separator: u32, - output: Witness, -) -> Result<(), OpcodeResolutionError> { - let scalars: Result, _> = - inputs.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); - let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); - - let res = backend.pedersen_hash(&scalars, domain_separator)?; - - insert_value(&output, res, initial_witness)?; - - Ok(()) -} diff --git a/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts b/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts index cfd5523b79f..aaa82f8f1e5 100644 --- a/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts +++ b/acvm-repo/acvm_js/test/browser/execute_circuit.test.ts @@ -75,16 +75,6 @@ it('successfully processes complex brillig foreign call opcodes', async () => { expect(solved_witness).to.be.deep.eq(expectedWitnessMap); }); -it('successfully executes a Pedersen opcode', async function () { - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/pedersen'); - - const solvedWitness: WitnessMap = await executeCircuit(bytecode, initialWitnessMap, () => { - throw Error('unexpected oracle'); - }); - - expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); -}); - it('successfully executes a MultiScalarMul opcode', async () => { const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/multi_scalar_mul'); diff --git a/acvm-repo/acvm_js/test/node/execute_circuit.test.ts b/acvm-repo/acvm_js/test/node/execute_circuit.test.ts index 1e3517e8814..120ad0fa738 100644 --- a/acvm-repo/acvm_js/test/node/execute_circuit.test.ts +++ b/acvm-repo/acvm_js/test/node/execute_circuit.test.ts @@ -76,17 +76,6 @@ it('successfully processes complex brillig foreign call opcodes', async () => { expect(solved_witness).to.be.deep.eq(expectedWitnessMap); }); -it('successfully executes a Pedersen opcode', async function () { - this.timeout(10000); - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/pedersen'); - - const solvedWitness: WitnessMap = await executeCircuit(bytecode, initialWitnessMap, () => { - throw Error('unexpected oracle'); - }); - - expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); -}); - it('successfully executes a MultiScalarMul opcode', async () => { const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/multi_scalar_mul'); @@ -117,25 +106,6 @@ it('successfully executes a MemoryOp opcode', async () => { expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); }); -it('successfully executes 500 pedersen circuits', async function () { - this.timeout(100000); - - // Pedersen opcodes used to have a large upfront cost due to generator calculation - // so we'd need to pass around the blackbox solver in JS to avoid redoing this work. - // - // This test now shows that we don't need to do this anymore without a performance regression. - - const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/pedersen'); - - for (let i = 0; i < 500; i++) { - const solvedWitness = await executeCircuit(bytecode, initialWitnessMap, () => { - throw Error('unexpected oracle'); - }); - - expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); - } -}); - /** * Below are all the same tests as above but using `executeProgram` * TODO: also add a couple tests for executing multiple circuits diff --git a/acvm-repo/acvm_js/test/shared/pedersen.ts b/acvm-repo/acvm_js/test/shared/pedersen.ts deleted file mode 100644 index 6e3ec403d65..00000000000 --- a/acvm-repo/acvm_js/test/shared/pedersen.ts +++ /dev/null @@ -1,13 +0,0 @@ -// See `pedersen_circuit` integration test in `acir/tests/test_program_serialization.rs`. -export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 74, 73, 10, 0, 0, 4, 180, 29, 252, 255, 193, 66, 40, 76, 77, 179, 34, 20, 36, - 136, 237, 83, 245, 101, 107, 79, 65, 94, 253, 214, 217, 255, 239, 192, 1, 43, 124, 181, 238, 113, 0, 0, 0, -]); - -export const initialWitnessMap = new Map([[1, '0x0000000000000000000000000000000000000000000000000000000000000001']]); - -export const expectedWitnessMap = new Map([ - [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [2, '0x083e7911d835097629f0067531fc15cafd79a89beecb39903f69572c636f4a5a'], - [3, '0x1a7f5efaad7f315c25a918f30cc8d7333fccab7ad7c90f14de81bcc528f9935d'], -]); diff --git a/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 0ee3a252840..869017f52ee 100644 --- a/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -14,16 +14,6 @@ pub trait BlackBoxFunctionSolver { signature: &[u8; 64], message: &[u8], ) -> Result; - fn pedersen_commitment( - &self, - inputs: &[F], - domain_separator: u32, - ) -> Result<(F, F), BlackBoxResolutionError>; - fn pedersen_hash( - &self, - inputs: &[F], - domain_separator: u32, - ) -> Result; fn multi_scalar_mul( &self, points: &[F], @@ -67,20 +57,6 @@ impl BlackBoxFunctionSolver for StubbedBlackBoxSolver { ) -> Result { Err(Self::fail(BlackBoxFunc::SchnorrVerify)) } - fn pedersen_commitment( - &self, - _inputs: &[F], - _domain_separator: u32, - ) -> Result<(F, F), BlackBoxResolutionError> { - Err(Self::fail(BlackBoxFunc::PedersenCommitment)) - } - fn pedersen_hash( - &self, - _inputs: &[F], - _domain_separator: u32, - ) -> Result { - Err(Self::fail(BlackBoxFunc::PedersenHash)) - } fn multi_scalar_mul( &self, _points: &[F], diff --git a/acvm-repo/bn254_blackbox_solver/benches/criterion.rs b/acvm-repo/bn254_blackbox_solver/benches/criterion.rs index cbcb75a3291..e7917fa1adc 100644 --- a/acvm-repo/bn254_blackbox_solver/benches/criterion.rs +++ b/acvm-repo/bn254_blackbox_solver/benches/criterion.rs @@ -13,22 +13,6 @@ fn bench_poseidon2(c: &mut Criterion) { c.bench_function("poseidon2", |b| b.iter(|| poseidon2_permutation(black_box(&inputs), 4))); } -fn bench_pedersen_commitment(c: &mut Criterion) { - let inputs = [FieldElement::one(); 2]; - - c.bench_function("pedersen_commitment", |b| { - b.iter(|| Bn254BlackBoxSolver.pedersen_commitment(black_box(&inputs), 0)) - }); -} - -fn bench_pedersen_hash(c: &mut Criterion) { - let inputs = [FieldElement::one(); 2]; - - c.bench_function("pedersen_hash", |b| { - b.iter(|| Bn254BlackBoxSolver.pedersen_hash(black_box(&inputs), 0)) - }); -} - fn bench_schnorr_verify(c: &mut Criterion) { let pub_key_x = FieldElement::from_hex( "0x04b260954662e97f00cab9adb773a259097f7a274b83b113532bce27fa3fb96a", @@ -62,7 +46,7 @@ fn bench_schnorr_verify(c: &mut Criterion) { criterion_group!( name = benches; config = Criterion::default().sample_size(40).measurement_time(Duration::from_secs(20)).with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = bench_poseidon2, bench_pedersen_commitment, bench_pedersen_hash, bench_schnorr_verify + targets = bench_poseidon2, bench_schnorr_verify ); criterion_main!(benches); diff --git a/acvm-repo/bn254_blackbox_solver/src/lib.rs b/acvm-repo/bn254_blackbox_solver/src/lib.rs index 6897116e90e..ec69c3797f6 100644 --- a/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -10,7 +10,6 @@ mod pedersen; mod poseidon2; mod schnorr; -use ark_ec::AffineRepr; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use generator::generators::derive_generators; pub use poseidon2::poseidon2_permutation; @@ -41,33 +40,6 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { )) } - fn pedersen_commitment( - &self, - inputs: &[FieldElement], - domain_separator: u32, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - let inputs: Vec = inputs.iter().map(|input| input.into_repr()).collect(); - let result = pedersen::commitment::commit_native_with_index(&inputs, domain_separator); - let result = if let Some((x, y)) = result.xy() { - (FieldElement::from_repr(*x), FieldElement::from_repr(*y)) - } else { - (FieldElement::from(0_u128), FieldElement::from(0_u128)) - }; - - Ok(result) - } - - fn pedersen_hash( - &self, - inputs: &[FieldElement], - domain_separator: u32, - ) -> Result { - let inputs: Vec = inputs.iter().map(|input| input.into_repr()).collect(); - let result = pedersen::hash::hash_with_index(&inputs, domain_separator); - let result = FieldElement::from_repr(result); - Ok(result) - } - fn multi_scalar_mul( &self, points: &[FieldElement], diff --git a/acvm-repo/brillig/src/black_box.rs b/acvm-repo/brillig/src/black_box.rs index 3887092a8c2..2b39e279aa8 100644 --- a/acvm-repo/brillig/src/black_box.rs +++ b/acvm-repo/brillig/src/black_box.rs @@ -61,13 +61,13 @@ pub enum BlackBoxOp { signature: HeapVector, result: MemoryAddress, }, - /// Calculates a Pedersen commitment to the inputs. + /// Deprecated. To be removed with a sync from aztec-packages PedersenCommitment { inputs: HeapVector, domain_separator: MemoryAddress, output: HeapArray, }, - /// Calculates a Pedersen hash to the inputs. + /// Deprecated. To be removed with a sync from aztec-packages PedersenHash { inputs: HeapVector, domain_separator: MemoryAddress, diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index 2053f4e7c86..544963b00db 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -232,41 +232,6 @@ pub(crate) fn evaluate_black_box ); Ok(()) } - BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { - let inputs: Vec = read_heap_vector(memory, inputs) - .iter() - .map(|x| *x.extract_field().unwrap()) - .collect(); - let domain_separator: u32 = - memory.read(*domain_separator).try_into().map_err(|_| { - BlackBoxResolutionError::Failed( - BlackBoxFunc::PedersenCommitment, - "Invalid signature length".to_string(), - ) - })?; - let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?; - memory.write_slice( - memory.read_ref(output.pointer), - &[MemoryValue::new_field(x), MemoryValue::new_field(y)], - ); - Ok(()) - } - BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { - let inputs: Vec = read_heap_vector(memory, inputs) - .iter() - .map(|x| *x.extract_field().unwrap()) - .collect(); - let domain_separator: u32 = - memory.read(*domain_separator).try_into().map_err(|_| { - BlackBoxResolutionError::Failed( - BlackBoxFunc::PedersenCommitment, - "Invalid signature length".to_string(), - ) - })?; - let hash = solver.pedersen_hash(&inputs, domain_separator)?; - memory.write(*output, MemoryValue::new_field(hash)); - Ok(()) - } BlackBoxOp::BigIntAdd { lhs, rhs, output } => { let lhs = memory.read(*lhs).try_into().unwrap(); let rhs = memory.read(*rhs).try_into().unwrap(); @@ -378,6 +343,8 @@ pub(crate) fn evaluate_black_box Ok(()) } + BlackBoxOp::PedersenCommitment { .. } => todo!("Deprecated Blackbox"), + BlackBoxOp::PedersenHash { .. } => todo!("Deprecated Blackbox"), } } @@ -392,8 +359,6 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { BlackBoxOp::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, BlackBoxOp::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, BlackBoxOp::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify, - BlackBoxOp::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, - BlackBoxOp::PedersenHash { .. } => BlackBoxFunc::PedersenHash, BlackBoxOp::MultiScalarMul { .. } => BlackBoxFunc::MultiScalarMul, BlackBoxOp::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, BlackBoxOp::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd, @@ -405,6 +370,8 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { BlackBoxOp::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation, BlackBoxOp::Sha256Compression { .. } => BlackBoxFunc::Sha256Compression, BlackBoxOp::ToRadix { .. } => unreachable!("ToRadix is not an ACIR BlackBoxFunc"), + BlackBoxOp::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, + BlackBoxOp::PedersenHash { .. } => BlackBoxFunc::PedersenHash, } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 367cdbe4973..c62365162ba 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -137,39 +137,6 @@ pub(crate) fn convert_black_box_call( ) } } - - BlackBoxFunc::PedersenCommitment => { - if let ( - [message, BrilligVariable::SingleAddr(domain_separator)], - [BrilligVariable::BrilligArray(result_array)], - ) = (function_arguments, function_results) - { - let message_vector = convert_array_or_vector(brillig_context, message, bb_func); - brillig_context.black_box_op_instruction(BlackBoxOp::PedersenCommitment { - inputs: message_vector.to_heap_vector(), - domain_separator: domain_separator.address, - output: result_array.to_heap_array(), - }); - } else { - unreachable!("ICE: Pedersen expects one array argument, a register for the domain separator, and one array result") - } - } - BlackBoxFunc::PedersenHash => { - if let ( - [message, BrilligVariable::SingleAddr(domain_separator)], - [BrilligVariable::SingleAddr(result)], - ) = (function_arguments, function_results) - { - let message_vector = convert_array_or_vector(brillig_context, message, bb_func); - brillig_context.black_box_op_instruction(BlackBoxOp::PedersenHash { - inputs: message_vector.to_heap_vector(), - domain_separator: domain_separator.address, - output: result.address, - }); - } else { - unreachable!("ICE: Pedersen hash expects one array argument, a register for the domain separator, and one register result") - } - } BlackBoxFunc::SchnorrVerify => { if let ( [BrilligVariable::SingleAddr(public_key_x), BrilligVariable::SingleAddr(public_key_y), BrilligVariable::BrilligArray(signature), message], @@ -424,6 +391,8 @@ pub(crate) fn convert_black_box_call( unreachable!("ICE: AES128Encrypt expects three array arguments, one array result") } } + BlackBoxFunc::PedersenCommitment => todo!("Deprecated Blackbox"), + BlackBoxFunc::PedersenHash => todo!("Deprecated Blackbox"), } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 9785e073be9..a0bf89fff0d 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -158,20 +158,7 @@ pub(crate) mod tests { ) -> Result { Ok(true) } - fn pedersen_commitment( - &self, - _inputs: &[FieldElement], - _domain_separator: u32, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Ok((2_u128.into(), 3_u128.into())) - } - fn pedersen_hash( - &self, - _inputs: &[FieldElement], - _domain_separator: u32, - ) -> Result { - Ok(6_u128.into()) - } + fn multi_scalar_mul( &self, _points: &[FieldElement], diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index b258905d657..a595584b376 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -347,24 +347,6 @@ impl DebugShow { result ); } - BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { - debug_println!( - self.enable_debug_trace, - " PEDERSEN {} {} -> {}", - inputs, - domain_separator, - output - ); - } - BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { - debug_println!( - self.enable_debug_trace, - " PEDERSEN_HASH {} {} -> {}", - inputs, - domain_separator, - output - ); - } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, @@ -462,6 +444,8 @@ impl DebugShow { output ); } + BlackBoxOp::PedersenCommitment { .. } => todo!("Deprecated Blackbox"), + BlackBoxOp::PedersenHash { .. } => todo!("Deprecated Blackbox"), } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 56b869fbf6b..e09f95508de 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1215,31 +1215,6 @@ impl AcirContext { ) -> Result, RuntimeError> { // Separate out any arguments that should be constants let (constant_inputs, constant_outputs) = match name { - BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => { - // The last argument of pedersen is the domain separator, which must be a constant - let domain_var = match inputs.pop() { - Some(domain_var) => domain_var.into_var()?, - None => { - return Err(RuntimeError::InternalError(InternalError::MissingArg { - name: "pedersen call".to_string(), - arg: "domain separator".to_string(), - call_stack: self.get_call_stack(), - })) - } - }; - - let domain_constant = match self.vars[&domain_var].as_constant() { - Some(domain_constant) => domain_constant, - None => { - return Err(RuntimeError::InternalError(InternalError::NotAConstant { - name: "domain separator".to_string(), - call_stack: self.get_call_stack(), - })) - } - }; - - (vec![*domain_constant], Vec::new()) - } BlackBoxFunc::Poseidon2Permutation => { // The last argument is the state length, which must be a constant let state_len = match inputs.pop() { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 9d271f7cd9c..6a1118de059 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -224,16 +224,6 @@ impl GeneratedAcir { output: outputs[0], } } - BlackBoxFunc::PedersenCommitment => BlackBoxFuncCall::PedersenCommitment { - inputs: inputs[0].clone(), - outputs: (outputs[0], outputs[1]), - domain_separator: constant_inputs[0].to_u128() as u32, - }, - BlackBoxFunc::PedersenHash => BlackBoxFuncCall::PedersenHash { - inputs: inputs[0].clone(), - output: outputs[0], - domain_separator: constant_inputs[0].to_u128() as u32, - }, BlackBoxFunc::EcdsaSecp256k1 => { BlackBoxFuncCall::EcdsaSecp256k1 { // 32 bytes for each public key co-ordinate @@ -371,6 +361,8 @@ impl GeneratedAcir { .expect("Compiler should generate correct size inputs"), outputs: outputs.try_into().expect("Compiler should generate correct size outputs"), }, + BlackBoxFunc::PedersenCommitment => todo!("Deprecated Blackbox"), + BlackBoxFunc::PedersenHash => todo!("Deprecated Blackbox"), }; self.push_opcode(AcirOpcode::BlackBoxFuncCall(black_box_func_call)); @@ -649,9 +641,7 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { | BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s - | BlackBoxFunc::Blake3 - | BlackBoxFunc::PedersenCommitment - | BlackBoxFunc::PedersenHash => None, + | BlackBoxFunc::Blake3 => None, BlackBoxFunc::Keccakf1600 => Some(25), // The permutation takes a fixed number of inputs, but the inputs length depends on the proving system implementation. @@ -687,6 +677,8 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { // FromLeBytes takes a variable array of bytes as input BlackBoxFunc::BigIntFromLeBytes => None, + BlackBoxFunc::PedersenCommitment => todo!(), + BlackBoxFunc::PedersenHash => todo!(), } } @@ -709,11 +701,6 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::Poseidon2Permutation => None, BlackBoxFunc::Sha256Compression => Some(8), - // Pedersen commitment returns a point - BlackBoxFunc::PedersenCommitment => Some(2), - - // Pedersen hash returns a field - BlackBoxFunc::PedersenHash => Some(1), // Can only apply a range constraint to one // witness at a time. @@ -743,6 +730,8 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { // AES encryption returns a variable number of outputs BlackBoxFunc::AES128Encrypt => None, + BlackBoxFunc::PedersenCommitment => todo!(), + BlackBoxFunc::PedersenHash => todo!(), } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index dfb8d0a8bf9..a9e3570ba0f 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -476,8 +476,6 @@ fn simplify_black_box_func( BlackBoxFunc::MultiScalarMul | BlackBoxFunc::SchnorrVerify - | BlackBoxFunc::PedersenCommitment - | BlackBoxFunc::PedersenHash | BlackBoxFunc::EmbeddedCurveAdd => { // Currently unsolvable here as we rely on an implementation in the backend. SimplifyResult::None @@ -503,6 +501,8 @@ fn simplify_black_box_func( } BlackBoxFunc::Sha256Compression => SimplifyResult::None, //TODO(Guillaume) BlackBoxFunc::AES128Encrypt => SimplifyResult::None, + BlackBoxFunc::PedersenCommitment => todo!("Deprecated Blackbox"), + BlackBoxFunc::PedersenHash => todo!("Deprecated Blackbox"), } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 58f70ba9192..c7ce3aaa155 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -1379,28 +1379,28 @@ mod test { fn should_not_merge_incorrectly_to_false() { // Regression test for #1792 // Tests that it does not simplify a true constraint an always-false constraint - // fn main f1 { - // b0(): - // v4 = call pedersen([Field 0], u32 0) - // v5 = array_get v4, index Field 0 - // v6 = cast v5 as u32 - // v8 = mod v6, u32 2 - // v9 = cast v8 as u1 - // v10 = allocate - // store Field 0 at v10 - // jmpif v9 then: b1, else: b2 - // b1(): - // v14 = add v5, Field 1 - // store v14 at v10 - // jmp b3() - // b3(): - // v12 = eq v9, u1 1 - // constrain v12 - // return - // b2(): - // store Field 0 at v10 - // jmp b3() - // } + // acir(inline) fn main f1 { + // b0(v0: [u8; 2]): + // v4 = call keccak256(v0, u8 2) + // v5 = array_get v4, index u8 0 + // v6 = cast v5 as u32 + // v8 = truncate v6 to 1 bits, max_bit_size: 32 + // v9 = cast v8 as u1 + // v10 = allocate + // store u8 0 at v10 + // jmpif v9 then: b2, else: b3 + // b2(): + // v12 = cast v5 as Field + // v13 = add v12, Field 1 + // store v13 at v10 + // jmp b4() + // b4(): + // constrain v9 == u1 1 + // return + // b3(): + // store u8 0 at v10 + // jmp b4() + // } let main_id = Id::test_new(1); let mut builder = FunctionBuilder::new("main".into(), main_id); @@ -1409,20 +1409,18 @@ mod test { let b2 = builder.insert_block(); let b3 = builder.insert_block(); - let element_type = Rc::new(vec![Type::field()]); - let array_type = Type::Array(element_type.clone(), 1); - - let zero = builder.field_constant(0_u128); - let zero_array = builder.array_constant(im::Vector::unit(zero), array_type); - let i_zero = builder.numeric_constant(0_u128, Type::unsigned(32)); - let pedersen = builder - .import_intrinsic_id(Intrinsic::BlackBox(acvm::acir::BlackBoxFunc::PedersenCommitment)); - let v4 = builder.insert_call( - pedersen, - vec![zero_array, i_zero], - vec![Type::Array(element_type, 2)], - )[0]; - let v5 = builder.insert_array_get(v4, zero, Type::field()); + let element_type = Rc::new(vec![Type::unsigned(8)]); + let array_type = Type::Array(element_type.clone(), 2); + let array = builder.add_parameter(array_type); + + let zero = builder.numeric_constant(0_u128, Type::unsigned(8)); + let two = builder.numeric_constant(2_u128, Type::unsigned(8)); + + let keccak = + builder.import_intrinsic_id(Intrinsic::BlackBox(acvm::acir::BlackBoxFunc::Keccak256)); + let v4 = + builder.insert_call(keccak, vec![array, two], vec![Type::Array(element_type, 32)])[0]; + let v5 = builder.insert_array_get(v4, zero, Type::unsigned(8)); let v6 = builder.insert_cast(v5, Type::unsigned(32)); let i_two = builder.numeric_constant(2_u128, Type::unsigned(32)); let v8 = builder.insert_binary(v6, BinaryOp::Mod, i_two); @@ -1435,7 +1433,9 @@ mod test { builder.switch_to_block(b1); let one = builder.field_constant(1_u128); - let v14 = builder.insert_binary(v5, BinaryOp::Add, one); + let v5b = builder.insert_cast(v5, Type::field()); + let v13: Id = builder.insert_binary(v5b, BinaryOp::Add, one); + let v14 = builder.insert_cast(v13, Type::unsigned(8)); builder.insert_store(v10, v14); builder.terminate_with_jmp(b3, vec![]); @@ -1449,8 +1449,9 @@ mod test { builder.insert_constrain(v12, v_true, None); builder.terminate_with_return(vec![]); - let ssa = builder.finish().flatten_cfg(); - let main = ssa.main(); + let ssa = builder.finish(); + let flattened_ssa = ssa.flatten_cfg(); + let main = flattened_ssa.main(); // Now assert that there is not an always-false constraint after flattening: let mut constrain_count = 0; diff --git a/noir_stdlib/src/embedded_curve_ops.nr b/noir_stdlib/src/embedded_curve_ops.nr index cd8c421e136..8e768b97479 100644 --- a/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir_stdlib/src/embedded_curve_ops.nr @@ -52,6 +52,14 @@ struct EmbeddedCurveScalar { hi: Field, } +impl EmbeddedCurveScalar { + #[field(bn254)] + fn from_field(scalar: Field) -> EmbeddedCurveScalar { + let (a,b) = crate::field::bn254::decompose(scalar); + EmbeddedCurveScalar { lo: a, hi: b } + } +} + // Computes a multi scalar multiplication over the embedded curve. // For bn254, We have Grumpkin and Baby JubJub. // For bls12-381, we have JubJub and Bandersnatch. diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index 270de210815..b72c1ecba8f 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -5,7 +5,7 @@ mod poseidon2; use crate::default::Default; use crate::uint128::U128; use crate::sha256::{digest, sha256_var}; -use crate::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar}; +use crate::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul}; #[foreign(sha256)] // docs:start:sha256 @@ -36,12 +36,14 @@ pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { } } -#[foreign(pedersen_commitment)] -pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} - pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { - let values = __pedersen_commitment_with_separator(input, separator); - EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: false } + let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N]; + for i in 0..N { + points[i] = EmbeddedCurveScalar::from_field(input[i]); + } + let generators = derive_generators("DEFAULT_DOMAIN_SEPARATOR".as_bytes(), separator); + let values = multi_scalar_mul(generators, points); + EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: values[2] as bool } } // docs:start:pedersen_hash @@ -65,7 +67,7 @@ fn __derive_generators(domain_separator_bytes: [u8; M], starting_index: u3 pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field { let v1 = pedersen_commitment(input); let length_generator :[EmbeddedCurvePoint;1] = derive_generators("pedersen_hash_length".as_bytes(), separator); - crate::embedded_curve_ops::multi_scalar_mul( + multi_scalar_mul( [length_generator[0], v1], [EmbeddedCurveScalar { lo: N as Field, hi: 0 }, EmbeddedCurveScalar { lo: 1, hi: 0 }] )[0] diff --git a/test_programs/compile_success_empty/intrinsic_die/src/main.nr b/test_programs/compile_success_empty/intrinsic_die/src/main.nr index c6e269c155d..17aaf02c283 100644 --- a/test_programs/compile_success_empty/intrinsic_die/src/main.nr +++ b/test_programs/compile_success_empty/intrinsic_die/src/main.nr @@ -1,6 +1,5 @@ // This test checks that we perform dead-instruction-elimination on intrinsic functions. fn main(x: Field) { - let hash = std::hash::pedersen_commitment([x]); let g1_x = 0x0000000000000000000000000000000000000000000000000000000000000001; let g1_y = 0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c; let g1 = std::embedded_curve_ops::EmbeddedCurvePoint { x: g1_x, y: g1_y, is_infinite: false }; diff --git a/tooling/lsp/src/solver.rs b/tooling/lsp/src/solver.rs index 0fcac73b905..3c2d7499880 100644 --- a/tooling/lsp/src/solver.rs +++ b/tooling/lsp/src/solver.rs @@ -16,14 +16,6 @@ impl BlackBoxFunctionSolver for WrapperSolver { self.0.schnorr_verify(public_key_x, public_key_y, signature, message) } - fn pedersen_commitment( - &self, - inputs: &[acvm::FieldElement], - domain_separator: u32, - ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { - self.0.pedersen_commitment(inputs, domain_separator) - } - fn multi_scalar_mul( &self, points: &[acvm::FieldElement], @@ -36,14 +28,6 @@ impl BlackBoxFunctionSolver for WrapperSolver { self.0.multi_scalar_mul(points, scalars_lo, scalars_hi) } - fn pedersen_hash( - &self, - inputs: &[acvm::FieldElement], - domain_separator: u32, - ) -> Result { - self.0.pedersen_hash(inputs, domain_separator) - } - fn ec_add( &self, input1_x: &acvm::FieldElement, From 318314d6dd35674328f534ebd882d4b0e66eab24 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 19 Jun 2024 19:58:42 -0500 Subject: [PATCH 53/57] fix: Add more thorough check for whether a type is valid when passing it from constrained code to unconstrained code (#5009) # Description ## Problem\* Resolves #5008 ## Summary\* Thank you to @nventuro for the minimal example and @TomAFrench for looking into this. As a result it was a quick fix. ## Additional Context All type system fixes must currently be duplicated in the elaborator so I've included this there as well. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: TomAFrench Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .../noirc_frontend/src/elaborator/lints.rs | 8 +--- .../noirc_frontend/src/hir/type_check/expr.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 42 +++++++++++++++++++ .../regression_5008/Nargo.toml | 7 ++++ .../regression_5008/src/main.nr | 17 ++++++++ 5 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 test_programs/compile_failure/regression_5008/Nargo.toml create mode 100644 test_programs/compile_failure/regression_5008/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/lints.rs b/compiler/noirc_frontend/src/elaborator/lints.rs index b86912940eb..af6f4cdb42f 100644 --- a/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/compiler/noirc_frontend/src/elaborator/lints.rs @@ -137,7 +137,7 @@ pub(super) fn unconstrained_function_args( function_args .iter() .filter_map(|(typ, _, span)| { - if type_contains_mutable_reference(typ) { + if !typ.is_valid_for_unconstrained_boundary() { Some(TypeCheckError::ConstrainedReferenceToUnconstrained { span: *span }) } else { None @@ -153,17 +153,13 @@ pub(super) fn unconstrained_function_return( ) -> Option { if return_type.contains_slice() { Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { span }) - } else if type_contains_mutable_reference(return_type) { + } else if !return_type.is_valid_for_unconstrained_boundary() { Some(TypeCheckError::UnconstrainedReferenceToConstrained { span }) } else { None } } -fn type_contains_mutable_reference(typ: &Type) -> bool { - matches!(&typ.follow_bindings(), Type::MutableReference(_)) -} - /// Only entrypoint functions require a `pub` visibility modifier applied to their return types. /// /// Application of `pub` to other functions is not meaningful and is a mistake. diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 46e8db8f5ff..a1302fd15ff 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -340,7 +340,7 @@ impl<'interner> TypeChecker<'interner> { // Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime if is_current_func_constrained && is_unconstrained_call { for (typ, _, _) in args.iter() { - if matches!(&typ.follow_bindings(), Type::MutableReference(_)) { + if !typ.is_valid_for_unconstrained_boundary() { self.errors.push(TypeCheckError::ConstrainedReferenceToUnconstrained { span }); } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ce36a22cf88..772558ec31a 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -788,6 +788,48 @@ impl Type { } } + /// Returns true if a value of this type can safely pass between constrained and + /// unconstrained functions (and vice-versa). + pub(crate) fn is_valid_for_unconstrained_boundary(&self) -> bool { + match self { + Type::FieldElement + | Type::Integer(_, _) + | Type::Bool + | Type::Unit + | Type::Constant(_) + | Type::Slice(_) + | Type::TypeVariable(_, _) + | Type::NamedGeneric(_, _) + | Type::Function(_, _, _) + | Type::FmtString(_, _) + | Type::Error => true, + + Type::MutableReference(_) + | Type::Forall(_, _) + | Type::Quoted(_) + | Type::TraitAsType(..) => false, + + Type::Alias(alias, generics) => { + let alias = alias.borrow(); + alias.get_type(generics).is_valid_for_unconstrained_boundary() + } + + Type::Array(length, element) => { + length.is_valid_for_unconstrained_boundary() + && element.is_valid_for_unconstrained_boundary() + } + Type::String(length) => length.is_valid_for_unconstrained_boundary(), + Type::Tuple(elements) => { + elements.iter().all(|elem| elem.is_valid_for_unconstrained_boundary()) + } + Type::Struct(definition, generics) => definition + .borrow() + .get_fields(generics) + .into_iter() + .all(|(_, field)| field.is_valid_for_unconstrained_boundary()), + } + } + /// Returns the number of `Forall`-quantified type variables on this type. /// Returns 0 if this is not a Type::Forall pub fn generic_count(&self) -> usize { diff --git a/test_programs/compile_failure/regression_5008/Nargo.toml b/test_programs/compile_failure/regression_5008/Nargo.toml new file mode 100644 index 00000000000..920c00660cf --- /dev/null +++ b/test_programs/compile_failure/regression_5008/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_5008" +type = "bin" +authors = [""] +compiler_version = ">=0.28.0" + +[dependencies] diff --git a/test_programs/compile_failure/regression_5008/src/main.nr b/test_programs/compile_failure/regression_5008/src/main.nr new file mode 100644 index 00000000000..6d9645ee6eb --- /dev/null +++ b/test_programs/compile_failure/regression_5008/src/main.nr @@ -0,0 +1,17 @@ +struct Bar { + value: Field, +} + +struct Foo{ + bar: &mut Bar, +} + +impl Foo { + unconstrained fn crash_fn(self) {} +} + +fn main() { + let foo = Foo { bar: &mut Bar { value: 0 } }; + + foo.crash_fn(); +} From 508e677cf2c66ac3427932a18f1661f5f4dc4202 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:05:18 +0100 Subject: [PATCH 54/57] feat: build simple dictionary from inspecting ACIR program (#5264) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/5262 ## Summary\* This PR pulls out a bunch of relevant constants from the ACIR/brillig bytecode so we can feed these into the fuzzer's strategy to ensure proper coverage of these values. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../fuzzer_checks/Nargo.toml | 5 + .../fuzzer_checks/src/main.nr | 6 + tooling/fuzzer/src/dictionary/mod.rs | 124 ++++++++++++++++++ tooling/fuzzer/src/lib.rs | 5 +- tooling/fuzzer/src/strategies/mod.rs | 36 ++--- tooling/fuzzer/src/strategies/uint.rs | 40 +++++- 6 files changed, 193 insertions(+), 23 deletions(-) create mode 100644 test_programs/noir_test_success/fuzzer_checks/Nargo.toml create mode 100644 test_programs/noir_test_success/fuzzer_checks/src/main.nr create mode 100644 tooling/fuzzer/src/dictionary/mod.rs diff --git a/test_programs/noir_test_success/fuzzer_checks/Nargo.toml b/test_programs/noir_test_success/fuzzer_checks/Nargo.toml new file mode 100644 index 00000000000..cd09d0d344d --- /dev/null +++ b/test_programs/noir_test_success/fuzzer_checks/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "fuzzer_checks" +type = "bin" +authors = [""] +[dependencies] diff --git a/test_programs/noir_test_success/fuzzer_checks/src/main.nr b/test_programs/noir_test_success/fuzzer_checks/src/main.nr new file mode 100644 index 00000000000..2b928db092e --- /dev/null +++ b/test_programs/noir_test_success/fuzzer_checks/src/main.nr @@ -0,0 +1,6 @@ + +#[test(should_fail_with = "42 is not allowed")] +fn finds_magic_value(x: u32) { + let x = x as u64; + assert(2 * x != 42, "42 is not allowed"); +} diff --git a/tooling/fuzzer/src/dictionary/mod.rs b/tooling/fuzzer/src/dictionary/mod.rs new file mode 100644 index 00000000000..bf2ab87be29 --- /dev/null +++ b/tooling/fuzzer/src/dictionary/mod.rs @@ -0,0 +1,124 @@ +//! This module defines how to build a dictionary of values which are likely to be correspond +//! to significant inputs during fuzzing by inspecting the [Program] being fuzzed. +//! +//! This dictionary can be fed into the fuzzer's [strategy][proptest::strategy::Strategy] in order to bias it towards +//! generating these values to ensure they get proper coverage. +use std::collections::HashSet; + +use acvm::{ + acir::{ + circuit::{ + brillig::{BrilligBytecode, BrilligInputs}, + directives::Directive, + opcodes::{BlackBoxFuncCall, FunctionInput}, + Circuit, Opcode, Program, + }, + native_types::Expression, + }, + brillig_vm::brillig::Opcode as BrilligOpcode, + AcirField, +}; + +/// Constructs a [HashSet] of values pulled from a [Program] which are likely to be correspond +/// to significant inputs during fuzzing. +pub(super) fn build_dictionary_from_program(program: &Program) -> HashSet { + let constrained_dictionaries = program.functions.iter().map(build_dictionary_from_circuit); + let unconstrained_dictionaries = + program.unconstrained_functions.iter().map(build_dictionary_from_unconstrained_function); + let dictionaries = constrained_dictionaries.chain(unconstrained_dictionaries); + + let mut constants: HashSet = HashSet::new(); + for dictionary in dictionaries { + constants.extend(dictionary); + } + constants +} + +fn build_dictionary_from_circuit(circuit: &Circuit) -> HashSet { + let mut constants: HashSet = HashSet::new(); + + fn insert_expr(dictionary: &mut HashSet, expr: &Expression) { + let quad_coefficients = expr.mul_terms.iter().map(|(k, _, _)| *k); + let linear_coefficients = expr.linear_combinations.iter().map(|(k, _)| *k); + let coefficients = linear_coefficients.chain(quad_coefficients); + + dictionary.extend(coefficients.clone()); + dictionary.insert(expr.q_c); + + // We divide the constant term by any coefficients in the expression to aid solving constraints such as `2 * x - 4 == 0`. + let scaled_constants = coefficients.map(|coefficient| expr.q_c / coefficient); + dictionary.extend(scaled_constants); + } + + fn insert_array_len(dictionary: &mut HashSet, array: &[T]) { + let array_length = array.len() as u128; + dictionary.insert(F::from(array_length)); + dictionary.insert(F::from(array_length - 1)); + } + + for opcode in &circuit.opcodes { + match opcode { + Opcode::AssertZero(expr) + | Opcode::Call { predicate: Some(expr), .. } + | Opcode::MemoryOp { predicate: Some(expr), .. } + | Opcode::Directive(Directive::ToLeRadix { a: expr, .. }) => { + insert_expr(&mut constants, expr) + } + + Opcode::MemoryInit { init, .. } => insert_array_len(&mut constants, init), + + Opcode::BrilligCall { inputs, predicate, .. } => { + for input in inputs { + match input { + BrilligInputs::Single(expr) => insert_expr(&mut constants, expr), + BrilligInputs::Array(exprs) => { + exprs.iter().for_each(|expr| insert_expr(&mut constants, expr)); + insert_array_len(&mut constants, exprs); + } + BrilligInputs::MemoryArray(_) => (), + } + } + if let Some(predicate) = predicate { + insert_expr(&mut constants, predicate) + } + } + + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { num_bits, .. }, + }) => { + let field = 1u128.wrapping_shl(*num_bits); + constants.insert(F::from(field)); + constants.insert(F::from(field - 1)); + } + _ => (), + } + } + + constants +} + +fn build_dictionary_from_unconstrained_function( + function: &BrilligBytecode, +) -> HashSet { + let mut constants: HashSet = HashSet::new(); + + for opcode in &function.bytecode { + match opcode { + BrilligOpcode::Cast { bit_size, .. } => { + let field = 1u128.wrapping_shl(*bit_size); + constants.insert(F::from(field)); + constants.insert(F::from(field - 1)); + } + BrilligOpcode::Const { bit_size, value, .. } => { + constants.insert(*value); + + let field = 1u128.wrapping_shl(*bit_size); + constants.insert(F::from(field)); + constants.insert(F::from(field - 1)); + } + _ => (), + } + } + + constants +} diff --git a/tooling/fuzzer/src/lib.rs b/tooling/fuzzer/src/lib.rs index 42dccc1dc83..28d7353f35a 100644 --- a/tooling/fuzzer/src/lib.rs +++ b/tooling/fuzzer/src/lib.rs @@ -4,9 +4,11 @@ //! Code is used under the MIT license. use acvm::{blackbox_solver::StubbedBlackBoxSolver, FieldElement}; +use dictionary::build_dictionary_from_program; use noirc_abi::InputMap; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; +mod dictionary; mod strategies; mod types; @@ -37,7 +39,8 @@ impl FuzzedExecutor { /// Fuzzes the provided program. pub fn fuzz(&self) -> FuzzTestResult { - let strategy = strategies::arb_input_map(&self.program.abi); + let dictionary = build_dictionary_from_program(&self.program.bytecode); + let strategy = strategies::arb_input_map(&self.program.abi, dictionary); let run_result: Result<(), TestError> = self.runner.clone().run(&strategy, |input_map| { diff --git a/tooling/fuzzer/src/strategies/mod.rs b/tooling/fuzzer/src/strategies/mod.rs index f5b03953ba8..46187a28d5b 100644 --- a/tooling/fuzzer/src/strategies/mod.rs +++ b/tooling/fuzzer/src/strategies/mod.rs @@ -5,28 +5,22 @@ use proptest::prelude::*; use acvm::{AcirField, FieldElement}; use noirc_abi::{input_parser::InputValue, Abi, AbiType, InputMap, Sign}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use uint::UintStrategy; mod int; mod uint; -proptest::prop_compose! { - pub(super) fn arb_field_from_integer(bit_size: u32)(value: u128)-> FieldElement { - let width = (bit_size % 128).clamp(1, 127); - let max_value = 2u128.pow(width) - 1; - let value = value % max_value; - FieldElement::from(value) - } -} - -pub(super) fn arb_value_from_abi_type(abi_type: &AbiType) -> SBoxedStrategy { +pub(super) fn arb_value_from_abi_type( + abi_type: &AbiType, + dictionary: HashSet, +) -> SBoxedStrategy { match abi_type { AbiType::Field => vec(any::(), 32) .prop_map(|bytes| InputValue::Field(FieldElement::from_be_bytes_reduce(&bytes))) .sboxed(), AbiType::Integer { width, sign } if sign == &Sign::Unsigned => { - UintStrategy::new(*width as usize) + UintStrategy::new(*width as usize, dictionary) .prop_map(|uint| InputValue::Field(uint.into())) .sboxed() } @@ -55,7 +49,7 @@ pub(super) fn arb_value_from_abi_type(abi_type: &AbiType) -> SBoxedStrategy { let length = *length as usize; - let elements = vec(arb_value_from_abi_type(typ), length..=length); + let elements = vec(arb_value_from_abi_type(typ, dictionary), length..=length); elements.prop_map(InputValue::Vec).sboxed() } @@ -63,7 +57,9 @@ pub(super) fn arb_value_from_abi_type(abi_type: &AbiType) -> SBoxedStrategy { let fields: Vec> = fields .iter() - .map(|(name, typ)| (Just(name.clone()), arb_value_from_abi_type(typ)).sboxed()) + .map(|(name, typ)| { + (Just(name.clone()), arb_value_from_abi_type(typ, dictionary.clone())).sboxed() + }) .collect(); fields @@ -75,17 +71,23 @@ pub(super) fn arb_value_from_abi_type(abi_type: &AbiType) -> SBoxedStrategy { - let fields: Vec<_> = fields.iter().map(arb_value_from_abi_type).collect(); + let fields: Vec<_> = + fields.iter().map(|typ| arb_value_from_abi_type(typ, dictionary.clone())).collect(); fields.prop_map(InputValue::Vec).sboxed() } } } -pub(super) fn arb_input_map(abi: &Abi) -> BoxedStrategy { +pub(super) fn arb_input_map( + abi: &Abi, + dictionary: HashSet, +) -> BoxedStrategy { let values: Vec<_> = abi .parameters .iter() - .map(|param| (Just(param.name.clone()), arb_value_from_abi_type(¶m.typ))) + .map(|param| { + (Just(param.name.clone()), arb_value_from_abi_type(¶m.typ, dictionary.clone())) + }) .collect(); values diff --git a/tooling/fuzzer/src/strategies/uint.rs b/tooling/fuzzer/src/strategies/uint.rs index 5021e832b97..94610dbc829 100644 --- a/tooling/fuzzer/src/strategies/uint.rs +++ b/tooling/fuzzer/src/strategies/uint.rs @@ -1,3 +1,6 @@ +use std::collections::HashSet; + +use acvm::{AcirField, FieldElement}; use proptest::{ strategy::{NewTree, Strategy}, test_runner::TestRunner, @@ -13,9 +16,12 @@ use rand::Rng; pub struct UintStrategy { /// Bit size of uint (e.g. 128) bits: usize, - + /// A set of fixtures to be generated + fixtures: Vec, /// The weight for edge cases (+/- 3 around 0 and max possible value) edge_weight: usize, + /// The weight for fixtures + fixtures_weight: usize, /// The weight for purely random values random_weight: usize, } @@ -24,8 +30,15 @@ impl UintStrategy { /// Create a new strategy. /// # Arguments /// * `bits` - Size of uint in bits - pub fn new(bits: usize) -> Self { - Self { bits, edge_weight: 10usize, random_weight: 50usize } + /// * `fixtures` - Set of `FieldElements` representing values which the fuzzer weight towards testing. + pub fn new(bits: usize, fixtures: HashSet) -> Self { + Self { + bits, + fixtures: fixtures.into_iter().collect(), + edge_weight: 10usize, + fixtures_weight: 40usize, + random_weight: 50usize, + } } fn generate_edge_tree(&self, runner: &mut TestRunner) -> NewTree { @@ -37,6 +50,22 @@ impl UintStrategy { Ok(proptest::num::u128::BinarySearch::new(start)) } + fn generate_fixtures_tree(&self, runner: &mut TestRunner) -> NewTree { + // generate random cases if there's no fixtures + if self.fixtures.is_empty() { + return self.generate_random_tree(runner); + } + + // Generate value tree from fixture. + let fixture = &self.fixtures[runner.rng().gen_range(0..self.fixtures.len())]; + if fixture.num_bits() <= self.bits as u32 { + return Ok(proptest::num::u128::BinarySearch::new(fixture.to_u128())); + } + + // If fixture is not a valid type, generate random value. + self.generate_random_tree(runner) + } + fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); let start = rng.gen_range(0..=self.type_max()); @@ -57,11 +86,12 @@ impl Strategy for UintStrategy { type Tree = proptest::num::u128::BinarySearch; type Value = u128; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { - let total_weight = self.random_weight + self.edge_weight; + let total_weight = self.random_weight + self.fixtures_weight + self.edge_weight; let bias = runner.rng().gen_range(0..total_weight); - // randomly select one of 2 strategies + // randomly select one of 3 strategies match bias { x if x < self.edge_weight => self.generate_edge_tree(runner), + x if x < self.edge_weight + self.fixtures_weight => self.generate_fixtures_tree(runner), _ => self.generate_random_tree(runner), } } From a30814f1f767bf874cd7e2969f5061c68f16b9a7 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:22:52 +0200 Subject: [PATCH 55/57] fix: handle struct with nested arrays in oracle return values (#5244) # Description ## Problem\* Resolves #5238 ## Summary\* Use nested values when the sizes do not match ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- acvm-repo/brillig_vm/src/lib.rs | 9 +++-- .../regression_4561/src/main.nr | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 862162ddccf..01f45bf653c 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -500,9 +500,14 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> VM<'a, F, B> { match output { ForeignCallParam::Array(values) => { if values.len() != *size { - return Err("Foreign call result array doesn't match expected size".to_string()); + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; + } else { + self.write_values_to_memory_slice(*pointer_index, values, value_types)?; } - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; } _ => { return Err("Function result size does not match brillig bytecode size".to_string()); diff --git a/test_programs/noir_test_success/regression_4561/src/main.nr b/test_programs/noir_test_success/regression_4561/src/main.nr index 70c447b49af..ad40941ff51 100644 --- a/test_programs/noir_test_success/regression_4561/src/main.nr +++ b/test_programs/noir_test_success/regression_4561/src/main.nr @@ -42,3 +42,37 @@ fn two_nested_return() { OracleMock::mock("two_nested_return").returns((0, [1, 2, 3, 4, 5, 6], 7, [1, 2, 3, 4, 5, 6])); assert_eq(two_nested_return_unconstrained(), (0, [[1, 2, 3], [4, 5, 6]], 7, [[1, 2, 3], [4, 5, 6]])); } + +#[oracle(foo_return)] +unconstrained fn foo_return() -> (Field, TReturn, TestTypeFoo) {} +unconstrained fn foo_return_unconstrained() -> (Field, TReturn, TestTypeFoo) { + foo_return() +} + +struct TestTypeFoo { + a: Field, + b: [[[Field; 3]; 4]; 2], + c: [TReturnElem; 2], + d: TReturnElem, +} + +#[test] +fn complexe_struct_return() { + OracleMock::mock("foo_return").returns( + ( + 0, [1, 2, 3, 4, 5, 6], 7, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [1, 2, 3, 4, 5, 6] + ) + ); + let foo_x = foo_return_unconstrained(); + assert_eq((foo_x.0, foo_x.1), (0, [[1, 2, 3], [4, 5, 6]])); + assert_eq(foo_x.2.a, 7); + assert_eq( + foo_x.2.b, [ + [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], [[13, 14, 15], [16, 17, 18], [19, 20, 21], [22, 23, 24]] + ] + ); + let a: TReturnElem = [1, 2, 3]; + let b: TReturnElem = [4, 5, 6]; + assert_eq(foo_x.2.c, [a, b]); + assert_eq(foo_x.2.d, a); +} From f761b7cb757154db3641e4fc1efb029b2ae30cca Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:51:32 +0100 Subject: [PATCH 56/57] chore: refactor test case generation in build.rs (#5280) # Description ## Problem\* Resolves ## Summary\* The `build.rs` file for generating the test cases is getting a bit unmanagable imo. This PR factors out some repeated code and adds a new helper function so we just need to pass in the body of the test. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/build.rs | 557 +++++++++++++++---------------------- 1 file changed, 228 insertions(+), 329 deletions(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 43c277ba03e..f2da161267d 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -64,392 +64,291 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ const IGNORED_NEW_FEATURE_TESTS: [&str; 3] = ["macros", "wildcard_type", "type_definition_annotation"]; -fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "execution_success"; +fn read_test_cases( + test_data_dir: &Path, + test_sub_dir: &str, +) -> impl Iterator { let test_data_dir = test_data_dir.join(test_sub_dir); - let test_case_dirs = fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); - for test_dir in test_case_dirs { + test_case_dirs.into_iter().map(|dir| { let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); + dir.file_name().into_string().expect("Directory can't be converted to string"); if test_name.contains('-') { panic!( "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" ); - }; - let test_dir = &test_dir.path(); - - let brillig_ignored = - if IGNORED_BRILLIG_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" }; - let new_features_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { - "\n#[ignore]" - } else { - "" - }; - - write!( - test_file, - r#" -#[test]{new_features_ignored} -fn execution_success_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force").arg("--use-legacy"); - - cmd.assert().success(); -}} + } + (test_name, dir.path()) + }) +} +fn generate_test_case( + test_file: &mut File, + test_type: &str, + test_name: &str, + test_dir: &std::path::Display, + test_content: &str, +) { + write!( + test_file, + r#" #[test] -fn execution_success_{test_name}() {{ +fn {test_type}_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force"); - - cmd.assert().success(); + let mut nargo = Command::cargo_bin("nargo").unwrap(); + nargo.arg("--program-dir").arg(test_program_dir); + {test_content} }} +"# + ) + .expect("Could not write templated test file."); +} -#[test]{brillig_ignored} -fn execution_success_{test_name}_brillig() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force").arg("--force-brillig"); +fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { + let test_type = "execution_success"; + let test_cases = read_test_cases(test_data_dir, test_type); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); - cmd.assert().success(); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + generate_test_case( + test_file, + test_type, + &test_name, + &test_dir, + r#" + nargo.arg("execute").arg("--force"); + + nargo.assert().success();"#, + ); + + if !IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + r#" + nargo.arg("execute").arg("--force").arg("--use-legacy"); + + nargo.assert().success();"#, + ); + } + + if !IGNORED_BRILLIG_TESTS.contains(&test_name.as_str()) { + generate_test_case( + test_file, + test_type, + &format!("{test_name}_brillig"), + &test_dir, + r#" + nargo.arg("execute").arg("--force").arg("--force-brillig"); + + nargo.assert().success();"#, + ); + } } } fn generate_execution_failure_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "execution_failure"; - let test_data_dir = test_data_dir.join(test_sub_dir); - - let test_case_dirs = - fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); - - for test_dir in test_case_dirs { - let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); - if test_name.contains('-') { - panic!( - "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" - ); - }; - let test_dir = &test_dir.path(); + let test_type = "execution_failure"; + let test_cases = read_test_cases(test_data_dir, test_type); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); - write!( + generate_test_case( test_file, + test_type, + &test_name, + &test_dir, r#" -#[test] -fn execution_failure_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force").arg("--use-legacy"); - - cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); -}} - -#[test] -fn execution_failure_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force"); + nargo.arg("execute").arg("--force"); + + nargo.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not());"#, + ); - cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + r#" + nargo.arg("execute").arg("--force").arg("--use-legacy"); + + nargo.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not());"#, + ); } } fn generate_noir_test_success_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "noir_test_success"; - let test_data_dir = test_data_dir.join(test_sub_dir); - - let test_case_dirs = - fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); + let test_type = "noir_test_success"; + let test_cases = read_test_cases(test_data_dir, "noir_test_success"); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); - for test_dir in test_case_dirs { - let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); - if test_name.contains('-') { - panic!( - "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" - ); - }; - let test_dir = &test_dir.path(); - - write!( + generate_test_case( test_file, + test_type, + &test_name, + &test_dir, r#" -#[test] -fn noir_test_success_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); + nargo.arg("test"); + + nargo.assert().success();"#, + ); - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test").arg("--use-legacy"); - - cmd.assert().success(); -}} - -#[test] -fn noir_test_success_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test"); - - cmd.assert().success(); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + r#" + nargo.arg("test").arg("--use-legacy"); + + nargo.assert().success();"#, + ); } } fn generate_noir_test_failure_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "noir_test_failure"; - let test_data_dir = test_data_dir.join(test_sub_dir); - - let test_case_dirs = - fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); - - for test_dir in test_case_dirs { - let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); - if test_name.contains('-') { - panic!( - "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" - ); - }; - let test_dir = &test_dir.path(); - - write!( + let test_type = "noir_test_failure"; + let test_cases = read_test_cases(test_data_dir, test_type); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); + generate_test_case( test_file, + test_type, + &test_name, + &test_dir, r#" -#[test] -fn noir_test_failure_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test").arg("--use-legacy"); - - cmd.assert().failure(); -}} - -#[test] -fn noir_test_failure_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("test"); + nargo.arg("test"); + + nargo.assert().failure();"#, + ); - cmd.assert().failure(); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + r#" + nargo.arg("test").arg("--use-legacy"); + + nargo.assert().failure();"#, + ); } } fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "compile_success_empty"; - let test_data_dir = test_data_dir.join(test_sub_dir); - - let test_case_dirs = - fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); - - for test_dir in test_case_dirs { - let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); - if test_name.contains('-') { - panic!( - "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" - ); - }; - let test_dir = &test_dir.path(); - - let new_feature_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { - "\n#[ignore]" - } else { - "" - }; - - write!( + let test_type = "compile_success_empty"; + let test_cases = read_test_cases(test_data_dir, test_type); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); + + let assert_zero_opcodes = r#" + let output = nargo.output().expect("Failed to execute command"); + + if !output.status.success() {{ + panic!("`nargo info` failed with: {}", String::from_utf8(output.stderr).unwrap_or_default()); + }} + + // `compile_success_empty` tests should be able to compile down to an empty circuit. + let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ + panic!("JSON was not well-formatted {:?}\n\n{:?}", e, std::str::from_utf8(&output.stdout)) + }}); + let num_opcodes = &json["programs"][0]["functions"][0]["acir_opcodes"]; + assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); + "#; + + generate_test_case( test_file, - r#" -#[test]{new_feature_ignored} -fn compile_success_empty_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("info"); - cmd.arg("--json"); - cmd.arg("--force"); - cmd.arg("--use-legacy"); - - let output = cmd.output().expect("Failed to execute command"); - - if !output.status.success() {{ - panic!("`nargo info` failed with: {{}}", String::from_utf8(output.stderr).unwrap_or_default()); - }} - - // `compile_success_empty` tests should be able to compile down to an empty circuit. - let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ - panic!("JSON was not well-formatted {{:?}}\n\n{{:?}}", e, std::str::from_utf8(&output.stdout)) - }}); - let num_opcodes = &json["programs"][0]["functions"][0]["acir_opcodes"]; - assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); -}} - -#[test] -fn compile_success_empty_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("info"); - cmd.arg("--json"); - cmd.arg("--force"); - - let output = cmd.output().expect("Failed to execute command"); - - if !output.status.success() {{ - panic!("`nargo info` failed with: {{}}", String::from_utf8(output.stderr).unwrap_or_default()); - }} - - // `compile_success_empty` tests should be able to compile down to an empty circuit. - let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ - panic!("JSON was not well-formatted {{:?}}\n\n{{:?}}", e, std::str::from_utf8(&output.stdout)) - }}); - let num_opcodes = &json["programs"][0]["functions"][0]["acir_opcodes"]; - assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + test_type, + &test_name, + &test_dir, + &format!( + r#" + nargo.arg("info").arg("--json").arg("--force"); + + {assert_zero_opcodes}"#, + ), + ); + + if !IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + &format!( + r#" + nargo.arg("info").arg("--json").arg("--force").arg("--use-legacy"); + + {assert_zero_opcodes}"#, + ), + ); + } } } fn generate_compile_success_contract_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "compile_success_contract"; - let test_data_dir = test_data_dir.join(test_sub_dir); - - let test_case_dirs = - fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); - - for test_dir in test_case_dirs { - let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); - if test_name.contains('-') { - panic!( - "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" - ); - }; - let test_dir = &test_dir.path(); + let test_type = "compile_success_contract"; + let test_cases = read_test_cases(test_data_dir, test_type); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); - write!( + generate_test_case( test_file, + test_type, + &test_name, + &test_dir, r#" -#[test] -fn compile_success_contract_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force").arg("--use-legacy"); + nargo.arg("compile").arg("--force"); + + nargo.assert().success();"#, + ); - cmd.assert().success(); -}} -#[test] -fn compile_success_contract_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force"); - - cmd.assert().success(); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + r#" + nargo.arg("compile").arg("--force").arg("--use-legacy"); + + nargo.assert().success();"#, + ); } } fn generate_compile_failure_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "compile_failure"; - let test_data_dir = test_data_dir.join(test_sub_dir); - - let test_case_dirs = - fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); + let test_type = "compile_failure"; + let test_cases = read_test_cases(test_data_dir, test_type); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); - for test_dir in test_case_dirs { - let test_name = - test_dir.file_name().into_string().expect("Directory can't be converted to string"); - if test_name.contains('-') { - panic!( - "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" - ); - }; - let test_dir = &test_dir.path(); - - let new_feature_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { - "\n#[ignore]" - } else { - "" - }; - - write!( + generate_test_case( test_file, - r#" -#[test]{new_feature_ignored} -fn compile_failure_legacy_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force").arg("--use-legacy"); - - cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); -}} -#[test] -fn compile_failure_{test_name}() {{ - let test_program_dir = PathBuf::from("{test_dir}"); - - let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force"); - - cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); -}} - "#, - test_dir = test_dir.display(), - ) - .expect("Could not write templated test file."); + test_type, + &test_name, + &test_dir, + r#"nargo.arg("compile").arg("--force"); + + nargo.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not());"#, + ); + + if !IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + generate_test_case( + test_file, + test_type, + &format!("legacy_{test_name}"), + &test_dir, + r#" + nargo.arg("compile").arg("--force").arg("--use-legacy"); + + nargo.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not());"#, + ); + } } } From 91a9b725cdb75c08cde888f49e7b8d11257e5de6 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:04:12 +0100 Subject: [PATCH 57/57] fix: fix incorrect return type being applied to stdlib functions `modulus_be_bytes()`, `modulus_be_bits()`, etc. (#5278) # Description ## Problem\* Resolves ## Summary\* We're currently returning an array literal for these methods in the stdlib whereas the Noir code states that it should return a slice. This results in issues when passing these "slices" into other functions such that it causes the inlining pass to panic. I've fixed this by replacing the array literal with a slice literal. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_frontend/src/monomorphization/mod.rs | 15 +++++++-------- .../execution_success/modulus/src/main.nr | 11 +++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 821ae7e7c4c..07820a70b3f 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1271,19 +1271,19 @@ impl<'interner> Monomorphizer<'interner> { } "modulus_le_bits" => { let bits = FieldElement::modulus().to_radix_le(2); - Some(self.modulus_array_literal(bits, IntegerBitSize::One, location)) + Some(self.modulus_slice_literal(bits, IntegerBitSize::One, location)) } "modulus_be_bits" => { let bits = FieldElement::modulus().to_radix_be(2); - Some(self.modulus_array_literal(bits, IntegerBitSize::One, location)) + Some(self.modulus_slice_literal(bits, IntegerBitSize::One, location)) } "modulus_be_bytes" => { let bytes = FieldElement::modulus().to_bytes_be(); - Some(self.modulus_array_literal(bytes, IntegerBitSize::Eight, location)) + Some(self.modulus_slice_literal(bytes, IntegerBitSize::Eight, location)) } "modulus_le_bytes" => { let bytes = FieldElement::modulus().to_bytes_le(); - Some(self.modulus_array_literal(bytes, IntegerBitSize::Eight, location)) + Some(self.modulus_slice_literal(bytes, IntegerBitSize::Eight, location)) } _ => None, }; @@ -1292,7 +1292,7 @@ impl<'interner> Monomorphizer<'interner> { None } - fn modulus_array_literal( + fn modulus_slice_literal( &self, bytes: Vec, arr_elem_bits: IntegerBitSize, @@ -1306,10 +1306,9 @@ impl<'interner> Monomorphizer<'interner> { Expression::Literal(Literal::Integer((byte as u128).into(), int_type.clone(), location)) }); - let typ = Type::Array(bytes_as_expr.len() as u32, Box::new(int_type)); - + let typ = Type::Slice(Box::new(int_type)); let arr_literal = ArrayLiteral { typ, contents: bytes_as_expr }; - Expression::Literal(Literal::Array(arr_literal)) + Expression::Literal(Literal::Slice(arr_literal)) } fn queue_function( diff --git a/test_programs/execution_success/modulus/src/main.nr b/test_programs/execution_success/modulus/src/main.nr index c7d6a2e2c7d..1627cc0dba2 100644 --- a/test_programs/execution_success/modulus/src/main.nr +++ b/test_programs/execution_success/modulus/src/main.nr @@ -3,6 +3,11 @@ fn main(bn254_modulus_be_bytes: [u8; 32], bn254_modulus_be_bits: [u1; 254]) { // NOTE: The constraints used in this circuit will only work when testing nargo with the plonk bn254 backend assert(modulus_size == 254); + assert_reverse( + std::field::modulus_be_bytes(), + std::field::modulus_le_bytes() + ); + let modulus_be_byte_array = std::field::modulus_be_bytes(); for i in 0..32 { assert(modulus_be_byte_array[i] == bn254_modulus_be_bytes[i]); @@ -21,3 +26,9 @@ fn main(bn254_modulus_be_bytes: [u8; 32], bn254_modulus_be_bits: [u1; 254]) { assert(modulus_le_bits[i] == bn254_modulus_be_bits[253 - i]); } } + +fn assert_reverse(forwards: [u8], backwards: [u8]) { + for i in 0..32 { + assert_eq(forwards[i], backwards[31 - i]); + } +}