noir-lang / noir

Noir is a domain specific language for zero knowledge proofs
https://noir-lang.org
Apache License 2.0
833 stars 178 forks source link

[Tracking Issue]: Macro discussions, library transforming and long term goals #2419

Open Maddiaa0 opened 11 months ago

Maddiaa0 commented 11 months ago

Problem

In recent discussions it was raised that noir will aim to support complex features that are required for smart contract languages. e,g, ways to support additions of syntactic sugar.

Why do we want this

It may be the case that teams (like aztec) would like to build meta-frameworks on top of noir. Much like in solana we have anchor lang which exists as a meta framework on top of rust.

Anchor abstracts the guts of the solana api and creates a nice developer framework that protects developers from common footguns and makes the devex alot nicer.

A quick example for this syntax is shown below: For example what originally was:

    fn mint(
        inputs: PrivateContextInputs,
        amount: Field, 
        owner: Field
    ) -> distinct pub abi::PrivateCircuitPublicInputs {
        let storage = Storage::init();
        let mut context = PrivateContext::new(inputs, abi::hash_args([amount, owner]));

        // Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
        let owner_balance = storage.balances.at(owner);
        send_note(&mut context, owner_balance, amount, owner);
        emit_unencrypted_log(&mut context, "Coins minted");

        // Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel..
        context.finish()
    }

can instead be written as:

   #[aztec(private)]
    fn mint(
        amount: Field, 
        owner: Field
    )  {
        let storage = Storage::init();

        // Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
        let owner_balance = storage.balances.at(owner);
        send_note(&mut context, owner_balance, amount, owner);
        emit_unencrypted_log(&mut context, "Coins minted");
    }

Custom annotations could be used (like macros) to transform the ast of functions and remove ugly and distracting boilerplate.

Short Term Goals (Hard deadline: completion by 15th September 2023)

Related Issues to the short term goals of this issue

Stretch goals support

Long term goals

The long term goals associated with this issue have an arbitrary time horizon and should not be direct blockers for stakeholders. They are rather aligned with supporting noir's long term vision.

The following work is not captured outside of this issue as no formal spec has been decided on, it is included to spark discussions about solutions that can remove stakeholder specific feature flags living in the code base

Abilities syntax

This syntax comes out of a conversation between kev and jake over how to remove the trans-formative attributes introduced in #2403.

The overarching idea of the abilities syntax is that we can polyfill arguments and variables into a method. The best mental model I have come across when thinking about it is that it is a combination between creating a facade class to inject arguments and a solidity modifier to extend a functions ability.

In the examples below, the with aztec3 will make a context available, and include the aztec kernel circuit inputs as the first parameter. Similar to how the hacky macro syntax above works, but at a language level.

Although nice solution, it will likely not exist in the short term due to its dependence on traits.

// (we should allow ‘using Aztec3’ on a contract to apply to each contract fn automatically?)
fn foo(x : PrivateField) with Aztec3 {
    x += 2; // impl Add for PrivateField
}

impl Add for PrivateField {
    fn add(self, other: Self) -> Self with Context {
        let context = Aztec3::get_mut_context();
        context.add(self, other);
    }
}

ability Aztec3 : Context {
    drop() { ... }
    contract _function_signauture(Context) -> [Field;12];
}

ability Context {
    get_mut_context() -> &mut Context { … }
}

fn foo(mut context : PrivateContext, x : Field) -> [Field; 12] {
       context.add_something();

       context.finalize()
}
Savio-Sou commented 11 months ago

Thanks for creating the issue!

@kevaundray should we replace / merge issues in https://github.com/noir-lang/noir/issues/1378 into this?