Skip to content

Commit

Permalink
Merge branch 'master' into tf/docs-on-release-pr
Browse files Browse the repository at this point in the history
  • Loading branch information
kevaundray authored Jan 24, 2024
2 parents c7ad1ae + 2645c10 commit c071223
Show file tree
Hide file tree
Showing 163 changed files with 8,574 additions and 161 deletions.
4 changes: 2 additions & 2 deletions .github/scripts/wasm-bindgen-install.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
set -eu

curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo-binstall wasm-bindgen-cli --version 0.2.86 -y
# TODO call this script directly
./scripts/install_wasm-bindgen.sh
6 changes: 3 additions & 3 deletions acvm-repo/brillig_vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> {
self.registers.set(register_index, value);
}

pub fn get_memory(&self) -> &Vec<Value> {
pub fn get_memory(&self) -> &[Value] {
self.memory.values()
}

Expand Down Expand Up @@ -748,7 +748,7 @@ mod tests {

let opcodes = [&start[..], &loop_body[..]].concat();
let vm = brillig_execute_and_get_vm(memory, &opcodes);
vm.get_memory().clone()
vm.get_memory().to_vec()
}

let memory = brillig_write_memory(vec![Value::from(0u128); 5]);
Expand Down Expand Up @@ -904,7 +904,7 @@ mod tests {

let opcodes = [&start[..], &recursive_fn[..]].concat();
let vm = brillig_execute_and_get_vm(memory, &opcodes);
vm.get_memory().clone()
vm.get_memory().to_vec()
}

let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]);
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/brillig_vm/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Memory {
}

/// Returns the values of the memory
pub fn values(&self) -> &Vec<Value> {
pub fn values(&self) -> &[Value] {
&self.inner
}
}
2 changes: 1 addition & 1 deletion compiler/noirc_driver/src/abi_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn into_abi_params(context: &Context, params: Vec<Param>) -> Vec<AbiParameter> {
// 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: &Vec<AbiParameter>,
abi_params: &[AbiParameter],
input_witnesses: Vec<Witness>,
) -> BTreeMap<String, Vec<Range<Witness>>> {
let mut idx = 0_usize;
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub enum ContractFunctionType {
Unconstrained,
}

#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompiledContract {
pub noir_version: String,

Expand All @@ -51,7 +51,7 @@ pub struct CompiledContract {
/// A contract function unlike a regular Noir program
/// however can have additional properties.
/// One of these being a function type.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractFunction {
pub name: String,

Expand Down
1 change: 0 additions & 1 deletion compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ pub(crate) fn optimize_into_acir(
.run_pass(Ssa::mem2reg, "After Mem2Reg:")
.run_pass(Ssa::fold_constants, "After Constant Folding:")
.run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:")
.run_pass(Ssa::bubble_up_constrains, "After Constraint Bubbling:")
.finish();

let brillig = ssa.to_brillig(print_brillig_trace);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1751,7 +1751,7 @@ fn execute_brillig(
// It may be finished, in-progress, failed, or may be waiting for results of a foreign call.
// If it's finished then we can omit the opcode and just write in the return values.
match vm_status {
VMStatus::Finished => Some((vm.get_registers().clone(), vm.get_memory().clone())),
VMStatus::Finished => Some((vm.get_registers().clone(), vm.get_memory().to_vec())),
VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"),
VMStatus::Failure { .. } => {
// TODO: Return an error stating that the brillig function failed.
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ impl Binary {
return SimplifyResult::SimplifiedTo(self.lhs);
}
if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) {
let zero = dfg.make_constant(FieldElement::zero(), Type::bool());
let zero = dfg.make_constant(FieldElement::zero(), operand_type);
return SimplifyResult::SimplifiedTo(zero);
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl NoirFunction {
pub fn function_attribute(&self) -> Option<&FunctionAttribute> {
self.def.attributes.function.as_ref()
}
pub fn secondary_attributes(&self) -> &Vec<SecondaryAttribute> {
pub fn secondary_attributes(&self) -> &[SecondaryAttribute] {
self.def.attributes.secondary.as_ref()
}
pub fn def(&self) -> &FunctionDefinition {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ fn type_check_functions(
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_methods_signatures(
resolver: &mut Resolver,
impl_methods: &Vec<(FileId, FuncId)>,
impl_methods: &[(FileId, FuncId)],
trait_id: TraitId,
trait_name_span: Span,
// These are the generics on the trait itself from the impl.
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1746,7 +1746,8 @@ impl<'a> Resolver<'a> {

// This resolves a static trait method T::trait_method by iterating over the where clause
//
// Returns the trait method, object type, and the trait generics.
// Returns the trait method, trait constraint, and whether the impl is assumed from a where
// clause. This is always true since this helper searches where clauses for a generic constraint.
// E.g. `t.method()` with `where T: Foo<Bar>` in scope will return `(Foo::method, T, vec![Bar])`
fn resolve_trait_method_by_named_generic(
&mut self,
Expand Down Expand Up @@ -1789,7 +1790,7 @@ impl<'a> Resolver<'a> {

// Try to resolve the given trait method path.
//
// Returns the trait method, object type, and the trait generics.
// Returns the trait method, trait constraint, and whether the impl is assumed to exist by a where clause or not
// E.g. `t.method()` with `where T: Foo<Bar>` in scope will return `(Foo::method, T, vec![Bar])`
fn resolve_trait_generic_path(
&mut self,
Expand Down
114 changes: 66 additions & 48 deletions compiler/noirc_frontend/src/hir/type_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
hir::{resolution::resolver::verify_mutable_reference, type_check::errors::Source},
hir_def::{
expr::{
self, HirArrayLiteral, HirBinaryOp, HirExpression, HirLiteral, HirMethodCallExpression,
HirMethodReference, HirPrefixExpression, ImplKind,
self, HirArrayLiteral, HirBinaryOp, HirExpression, HirIdent, HirLiteral,
HirMethodCallExpression, HirMethodReference, HirPrefixExpression, ImplKind,
},
types::Type,
},
Expand Down Expand Up @@ -46,50 +46,7 @@ impl<'interner> TypeChecker<'interner> {
/// function `foo` to refer to.
pub(crate) fn check_expression(&mut self, expr_id: &ExprId) -> Type {
let typ = match self.interner.expression(expr_id) {
HirExpression::Ident(ident) => {
// An identifiers type may be forall-quantified in the case of generic functions.
// E.g. `fn foo<T>(t: T, field: Field) -> T` has type `forall T. fn(T, Field) -> T`.
// We must instantiate identifiers at every call site to replace this T with a new type
// variable to handle generic functions.
let t = self.interner.id_type_substitute_trait_as_type(ident.id);

// This instantiate's a trait's generics as well which need to be set
// when the constraint below is later solved for when the function is
// finished. How to link the two?
let (typ, bindings) = t.instantiate(self.interner);

// Push any trait constraints required by this definition to the context
// to be checked later when the type of this variable is further constrained.
if let Some(definition) = self.interner.try_definition(ident.id) {
if let DefinitionKind::Function(function) = definition.kind {
let function = self.interner.function_meta(&function);

for mut constraint in function.trait_constraints.clone() {
constraint.apply_bindings(&bindings);
self.trait_constraints.push((constraint, *expr_id));
}
}
}

if let ImplKind::TraitMethod(_, mut constraint, assumed) = ident.impl_kind {
constraint.apply_bindings(&bindings);
if assumed {
let trait_impl = TraitImplKind::Assumed {
object_type: constraint.typ,
trait_generics: constraint.trait_generics,
};
self.interner.select_impl_for_expression(*expr_id, trait_impl);
} else {
// Currently only one impl can be selected per expr_id, so this
// constraint needs to be pushed after any other constraints so
// that monomorphization can resolve this trait method to the correct impl.
self.trait_constraints.push((constraint, *expr_id));
}
}

self.interner.store_instantiation_bindings(*expr_id, bindings);
typ
}
HirExpression::Ident(ident) => self.check_ident(ident, expr_id),
HirExpression::Literal(literal) => {
match literal {
HirLiteral::Array(HirArrayLiteral::Standard(arr)) => {
Expand Down Expand Up @@ -341,6 +298,67 @@ impl<'interner> TypeChecker<'interner> {
typ
}

/// Returns the type of the given identifier
fn check_ident(&mut self, ident: HirIdent, expr_id: &ExprId) -> Type {
let mut bindings = TypeBindings::new();

// Add type bindings from any constraints that were used.
// We need to do this first since otherwise instantiating the type below
// will replace each trait generic with a fresh type variable, rather than
// the type used in the trait constraint (if it exists). See #4088.
if let ImplKind::TraitMethod(_, constraint, _) = &ident.impl_kind {
let the_trait = self.interner.get_trait(constraint.trait_id);
assert_eq!(the_trait.generics.len(), constraint.trait_generics.len());

for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) {
bindings.insert(param.id(), (param.clone(), arg.clone()));
}
}

// An identifiers type may be forall-quantified in the case of generic functions.
// E.g. `fn foo<T>(t: T, field: Field) -> T` has type `forall T. fn(T, Field) -> T`.
// We must instantiate identifiers at every call site to replace this T with a new type
// variable to handle generic functions.
let t = self.interner.id_type_substitute_trait_as_type(ident.id);

// This instantiates a trait's generics as well which need to be set
// when the constraint below is later solved for when the function is
// finished. How to link the two?
let (typ, bindings) = t.instantiate_with_bindings(bindings, self.interner);

// Push any trait constraints required by this definition to the context
// to be checked later when the type of this variable is further constrained.
if let Some(definition) = self.interner.try_definition(ident.id) {
if let DefinitionKind::Function(function) = definition.kind {
let function = self.interner.function_meta(&function);

for mut constraint in function.trait_constraints.clone() {
constraint.apply_bindings(&bindings);
self.trait_constraints.push((constraint, *expr_id));
}
}
}

if let ImplKind::TraitMethod(_, mut constraint, assumed) = ident.impl_kind {
constraint.apply_bindings(&bindings);
if assumed {
let trait_impl = TraitImplKind::Assumed {
object_type: constraint.typ,
trait_generics: constraint.trait_generics,
};
self.interner.select_impl_for_expression(*expr_id, trait_impl);
} else {
// Currently only one impl can be selected per expr_id, so this
// constraint needs to be pushed after any other constraints so
// that monomorphization can resolve this trait method to the correct impl.
self.trait_constraints.push((constraint, *expr_id));
}
}

self.interner.store_instantiation_bindings(*expr_id, bindings);
typ
}

pub fn verify_trait_constraint(
&mut self,
object_type: &Type,
Expand Down Expand Up @@ -1010,9 +1028,9 @@ impl<'interner> TypeChecker<'interner> {

fn bind_function_type_impl(
&mut self,
fn_params: &Vec<Type>,
fn_params: &[Type],
fn_ret: &Type,
callsite_args: &Vec<(Type, ExprId, Span)>,
callsite_args: &[(Type, ExprId, Span)],
span: Span,
) -> Type {
if fn_params.len() != callsite_args.len() {
Expand Down
5 changes: 3 additions & 2 deletions compiler/wasm/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ pub fn compile(
})?
.0;

let optimized_contract = nargo::ops::optimize_contract(compiled_contract, expression_width);
let optimized_contract =
nargo::ops::transform_contract(compiled_contract, expression_width);

let compile_output = generate_contract_artifact(optimized_contract);
Ok(JsCompileResult::new(compile_output))
Expand All @@ -205,7 +206,7 @@ pub fn compile(
})?
.0;

let optimized_program = nargo::ops::optimize_program(compiled_program, expression_width);
let optimized_program = nargo::ops::transform_program(compiled_program, expression_width);

let compile_output = generate_program_artifact(optimized_program);
Ok(JsCompileResult::new(compile_output))
Expand Down
4 changes: 2 additions & 2 deletions compiler/wasm/src/compile_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl CompilerContext {
})?
.0;

let optimized_program = nargo::ops::optimize_program(compiled_program, np_language);
let optimized_program = nargo::ops::transform_program(compiled_program, np_language);

let compile_output = generate_program_artifact(optimized_program);
Ok(JsCompileResult::new(compile_output))
Expand All @@ -134,7 +134,7 @@ impl CompilerContext {
})?
.0;

let optimized_contract = nargo::ops::optimize_contract(compiled_contract, np_language);
let optimized_contract = nargo::ops::transform_contract(compiled_contract, np_language);

let compile_output = generate_contract_artifact(optimized_contract);
Ok(JsCompileResult::new(compile_output))
Expand Down
57 changes: 57 additions & 0 deletions docs/versioned_docs/version-v0.23.0/explainers/explainer-oracle.md
Original file line number Diff line number Diff line change
@@ -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.
Loading

0 comments on commit c071223

Please sign in to comment.