Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,4 @@
"**/l1-contracts/lib/**": true,
"**/barretenberg/cpp/build*/**": true
},
"cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp",
"noir.nargoPath": "./noir/noir-repo/target/release/nargo"
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ fn check_for_compute_note_hash_and_nullifier_definition(
FunctionReturnType::Default(_) => false,
FunctionReturnType::Ty(unresolved_type) => {
match &unresolved_type.typ {
UnresolvedTypeData::Array(_, inner_type) => matches!(inner_type.typ, UnresolvedTypeData::FieldElement),
UnresolvedTypeData::Unconstrained(inner_type) => match inner_type.to_owned().typ {
UnresolvedTypeData::Array(_, elements) => matches!(elements.typ.to_owned(), UnresolvedTypeData::FieldElement),
_ => false
},
_ => false,
}
}
Expand Down Expand Up @@ -179,7 +182,7 @@ fn generate_compute_note_hash_and_nullifier_source(
storage_slot: Field,
note_type_id: Field,
serialized_note: [Field; {}]
) -> pub [Field; 4] {{
) -> pub Unconstrained<[Field; 4]> {{
assert(false, \"This contract does not use private notes\");
[0, 0, 0, 0]
}}",
Expand Down Expand Up @@ -210,7 +213,7 @@ fn generate_compute_note_hash_and_nullifier_source(
storage_slot: Field,
note_type_id: Field,
serialized_note: [Field; {}]
) -> pub [Field; 4] {{
) -> pub Unconstrained<[Field; 4]> {{
let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot);

{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call
"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));",
dep::aztec::oracle::arguments::pack_arguments(args_acc).make_constrained(| packed_hash | {{
assert(args_hash == packed_hash);
}});",
call_args
)
} else {
Expand Down
5 changes: 4 additions & 1 deletion noir/noir-repo/compiler/noirc_evaluator/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("Called from a constrained runtime")]
OnlyWithinUnconstrained { call_stack: CallStack },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -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::OnlyWithinUnconstrained { call_stack } => call_stack,
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ pub(crate) fn optimize_into_acir(
.run_pass(Ssa::defunctionalize, "After Defunctionalization:")
.run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:")
.run_pass(Ssa::inline_functions, "After Inlining:")
.run_pass(Ssa::resolve_is_unconstrained, "After Resolving IsUnconstrained:")
.try_run_pass(
Ssa::resolve_runtime_checks,
"After Resolving runtime checks (is_unconstrained, assert_unconstrained):",
)?
// Run mem2reg with the CFG separated into blocks
.run_pass(Ssa::mem2reg, "After Mem2Reg:")
.run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub(crate) enum Intrinsic {
AsField,
AsWitness,
IsUnconstrained,
AssertUnconstrained,
}

impl std::fmt::Display for Intrinsic {
Expand All @@ -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::AssertUnconstrained => write!(f, "assert_unconstrained"),
}
}
}
Expand All @@ -102,9 +104,10 @@ impl Intrinsic {
/// If there are no side effects then the `Intrinsic` can be removed if the result is unused.
pub(crate) fn has_side_effects(&self) -> bool {
match self {
Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint | Intrinsic::AsWitness => {
true
}
Intrinsic::AssertConstant
| Intrinsic::ApplyRangeConstraint
| Intrinsic::AsWitness
| Intrinsic::AssertUnconstrained => true,

// These apply a constraint that the input must fit into a specified number of limbs.
Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => true,
Expand Down Expand Up @@ -150,6 +153,7 @@ impl Intrinsic {
"as_field" => Some(Intrinsic::AsField),
"as_witness" => Some(Intrinsic::AsWitness),
"is_unconstrained" => Some(Intrinsic::IsUnconstrained),
"assert_unconstrained" => Some(Intrinsic::AssertUnconstrained),
other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ pub(super) fn simplify_call(
}
Intrinsic::AsWitness => SimplifyResult::None,
Intrinsic::IsUnconstrained => SimplifyResult::None,
Intrinsic::AssertUnconstrained => SimplifyResult::None,
}
}

Expand Down
2 changes: 1 addition & 1 deletion noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ mod rc;
mod remove_bit_shifts;
mod remove_enable_side_effects;
mod remove_if_else;
mod resolve_is_unconstrained;
mod resolve_runtime_checks;
mod simplify_cfg;
mod unrolling;
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ impl Context {
| Intrinsic::AsField
| Intrinsic::AsSlice
| Intrinsic::AsWitness
| Intrinsic::IsUnconstrained => false,
| Intrinsic::IsUnconstrained
| Intrinsic::AssertUnconstrained => false,
},

// We must assume that functions contain a side effect as we cannot inspect more deeply.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ fn slice_capacity_change(
| Intrinsic::FromField
| Intrinsic::AsField
| Intrinsic::AsWitness
| Intrinsic::IsUnconstrained => SizeChange::None,
| Intrinsic::IsUnconstrained
| Intrinsic::AssertUnconstrained => SizeChange::None,
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::ssa::{
ir::{
function::{Function, RuntimeType},
instruction::{Instruction, Intrinsic},
types::Type,
value::Value,
use crate::{
errors::RuntimeError,
ssa::{
ir::{
function::{Function, RuntimeType},
instruction::{Instruction, Intrinsic},
types::Type,
value::Value,
},
ssa_gen::Ssa,
},
ssa_gen::Ssa,
};
use acvm::FieldElement;
use fxhash::FxHashSet as HashSet;
Expand All @@ -15,11 +18,12 @@ impl Ssa {
/// with the resolved boolean value.
/// Note that this pass must run after the pass that does runtime separation, since in SSA generation an ACIR function can end up targeting brillig.
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn resolve_is_unconstrained(mut self) -> Self {
pub(crate) fn resolve_runtime_checks(mut self) -> Result<Ssa, RuntimeError> {
for func in self.functions.values_mut() {
replace_is_unconstrained_result(func);
assert_unconstrained_calls(func)?;
}
self
Ok(self)
}
}

Expand Down Expand Up @@ -54,3 +58,33 @@ fn replace_is_unconstrained_result(func: &mut Function) {
func.dfg.set_value_from_id(original_return_id, is_within_unconstrained);
}
}

fn assert_unconstrained_calls(func: &mut Function) -> Result<(), RuntimeError> {
let is_within_unconstrained = matches!(func.runtime(), RuntimeType::Brillig);
for block_id in func.reachable_blocks() {
let instructions = func.dfg[block_id].take_instructions();
let mut filtered_instructions = Vec::with_capacity(instructions.len());

for instruction_id in instructions {
let target_func = match &func.dfg[instruction_id] {
Instruction::Call { func, .. } => *func,
_ => {
filtered_instructions.push(instruction_id);
continue;
}
};
if let Value::Intrinsic(Intrinsic::AssertUnconstrained) = &func.dfg[target_func] {
if !is_within_unconstrained {
return Err(RuntimeError::OnlyWithinUnconstrained {
call_stack: func.dfg.get_call_stack(instruction_id),
});
}
} else {
filtered_instructions.push(instruction_id);
}
}

*func.dfg[block_id].instructions_mut() = filtered_instructions;
}
Ok(())
}
7 changes: 6 additions & 1 deletion noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ pub enum UnresolvedTypeData {
// The type of quoted code for metaprogramming
Code,

Unspecified, // This is for when the user declares a variable without specifying it's type
Unspecified, // This is for when the user declares a variable without specifying it's type,

Unconstrained(Box<UnresolvedType>),
Error,
}

Expand Down Expand Up @@ -216,6 +218,9 @@ impl std::fmt::Display for UnresolvedTypeData {
Error => write!(f, "error"),
Unspecified => write!(f, "unspecified"),
Parenthesized(typ) => write!(f, "({typ})"),
Unconstrained(arg) => {
write!(f, "Unconstrained<{}>", arg)
}
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ use crate::{
traits::TraitConstraint,
},
macros_api::{
HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness,
UnaryOp, UnresolvedType, UnresolvedTypeData,
HirExpression, HirLiteral, HirStatement, Ident, Path, PathKind, SecondaryAttribute,
Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData,
},
node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId},
token::{Keyword, Token},
Generics, Type, TypeBinding, TypeVariable, TypeVariableKind,
};

Expand Down Expand Up @@ -118,6 +119,14 @@ impl<'context> Elaborator<'context> {
Type::MutableReference(Box::new(self.resolve_type_inner(*element, new_variables)))
}
Parenthesized(typ) => self.resolve_type_inner(*typ, new_variables),
Unconstrained(arg) => self.resolve_named_type(
Path::from_ident(Ident::from_token(
Token::Ident(Keyword::UnconstrainedType.to_string()),
arg.span.unwrap_or_default(),
)),
vec![*arg],
new_variables,
),
};

if let Type::Struct(_, _) = resolved_type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::hir_def::expr::{

use crate::hir_def::traits::{Trait, TraitConstraint};
use crate::macros_api::SecondaryAttribute;
use crate::token::{Attributes, FunctionAttribute};
use crate::token::{Attributes, FunctionAttribute, Keyword, Token};
use regex::Regex;
use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::rc::Rc;
Expand Down Expand Up @@ -603,6 +603,14 @@ impl<'a> Resolver<'a> {
Type::MutableReference(Box::new(self.resolve_type_inner(*element, new_variables)))
}
Parenthesized(typ) => self.resolve_type_inner(*typ, new_variables),
Unconstrained(arg) => self.resolve_named_type(
Path::from_ident(Ident::from_token(
Token::Ident(Keyword::UnconstrainedType.to_string()),
arg.span.unwrap_or_default(),
)),
vec![*arg],
new_variables,
),
};

if let Type::Struct(_, _) = resolved_type {
Expand Down
Loading