Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: devex improvements to noirjs #3044

Closed
wants to merge 9 commits into from
Closed

Conversation

signorecello
Copy link
Contributor

@signorecello signorecello commented Oct 9, 2023

Description

Working on autogenerated docs at #3035 I came up with some improvements to UX and Devex for NoirJS.

Problem*

Not really a problem, just addressing some DevEx concerns and UX. Before this PR, standard usage was:

const backend = new BarretenbergBackend(circuit, 4);
const noir = new Noir(circuit, backend);

This could be improved

Summary*

  • It adds the destroy method to proxy to backend_barretenberg, saving user's resources.
  • Adds an "options" object that is passed to the initialisation of barretenberg:
// previously
new Backend(circuit, 4) // what's the 4?

// now
new Backend(circuit { numOfThreads : 4}) // oh it's the number of threads!
  • Adds the circuit parameter to the new execute function instead of the Noir class - this avoids creating a new Noir class everytime we want to execute something, plus removes the need to always pass a circuit twice (to backend, and to Noir)
  • Makes it not export acvm, abi, generateWitness, and others from the index file of noir_js, as a precursor to chore: adding autogenerated docs for noirjs #3035 It will still export these and we'll figure it out with the docs autogenerator
  • Separated the Backend type into two interfaces: Backend and BackendInternal so it's clearer what's supposed to be called by the Noir class and what's not yet implemented there. Also precursor to chore: adding autogenerated docs for noirjs #3035

@TomAFrench
Copy link
Member

Noir only instantiates the Backend once: at init. This makes the user UX faster as it's not called every time we prove or verify something.

We would only instantiate if the backend hadn't already been instantiated previously. This change saves us checking whether a variable is defined or not but means we give up the option of lazy initialization. I don't think that this is a good trade.

@TomAFrench
Copy link
Member

Noir doesn't take in the circuit in the constructor. Instead, since we're passing it to the Backend, we can just call this.backend.circuit.

This requires us to always have a backend when interacting with the Noir class. I can see situations where you would want to create an interact with a Noir program without instantiating a backend at all (e.g. I want to execute a program to get its return value but I don't want to create a proof), it would be good to be able to just omit the backend in these cases but adding this reliance would prevent this.

@TomAFrench
Copy link
Member

Makes it not export acvm, abi, generateWitness, and others from the index file of noir_js

I also think this is not ideal. We should give users the tools to break out of the standard workflow if necessary (we've already run into a couple of cases where this is necessary).

@signorecello
Copy link
Contributor Author

We would only instantiate if the backend hadn't already been instantiated previously. This change saves us checking whether a variable is defined or not but means we give up the option of lazy initialization. I don't think that this is a good trade.

On the other hand it kind of relies on the developer to store the instance if they're browsing away to other routes. But I can revert to lazy initialisation if you prefer.

it would be good to be able to just omit the backend in these cases but adding this reliance would prevent this.

I see what you mean, but I don't think passing the circuit twice is good practice. We want to target both the "standard" flow, and the more advanced usage.

Just as you suggested, I can change to make backend an optional parameter (and do some error handling) and tackle both situations.

We should give users the tools to break out of the standard workflow if necessary

I most definitely agree with you! However, this doesn't void the user of the possibility to use these packages, as he can always import the acvm and abi packages.

If we export them as an API, they'd be clogging the inexperienced developer's workflow with use-cases that are probably not the majority.

@TomAFrench
Copy link
Member

he can always import the acvm and abi packages.

This kinda defeats the point of noir.js, no? If we're expecting users to manage versions of the underlying packages to keep them in sync with each other, and as well as that with the versions included in noir.js then we've just made the problem worse.

@signorecello
Copy link
Contributor Author

I actually raise the same point, but with a different argumentation... If noir_js exists just to export correct versions of acvm and abi, then that would be defeating the purpose.

In other words, noir_js exists exactly for the vast majority of users who wouldn't have a use for importing and managing acvm or abi versions. It's meant to tackle 90% of the use cases, while not leaving behind advanced users.

We're not excluding advanced developers from using our tools, we're including everybody else.

@TomAFrench
Copy link
Member

If noir_js exists just to export correct versions of acvm and abi, then that would be defeating the purpose.

I didn't say that it's the only purpose but a significant motivator for noir_js was to provide a package with a known-consistent set of dependencies.

@signorecello signorecello mentioned this pull request Oct 10, 2023
@signorecello
Copy link
Contributor Author

cc #2910

@signorecello
Copy link
Contributor Author

signorecello commented Oct 10, 2023

So talking to @kevaundray we decided to export everything (and shut up the docs autogenerator) since we're not sure which methods will be used elsewhere, there's some work going on in Aztec that would be using some acvm methods.

Once that's cleared up, we can narrow them as part of #3078

Let me just make some changes and I'll push & resolve conflicts. Thanks @TomAFrench for the discussion

@Savio-Sou
Copy link
Collaborator

Thanks @signorecello! Do request for re-reviews when ready.

Copy link
Member

@TomAFrench TomAFrench left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR is muddling what the Noir class represents, it encapsulates a Noir program and should expose methods to interact with this program. new Noir() then doesn't really have any meaning and any method calls on Noir() could just be regular functions.

I think making the Backend be the source of truth on the circuit in question is also a misstep. The Backend only needs the circuit to determine how much of the CRS to load and is otherwise generic, ideally I should be able to reuse the same backend object for creating proofs for two different circuits but this PR takes us further away from this.

tooling/noir_js/src/index.ts Show resolved Hide resolved
// Initial inputs to your program
async execute(
inputs: abi.InputMap,
circuit?: CompiledCircuit,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a better API to instantiate the Noir class with a CompiledCircuit and use that in this function rather than having to pass in a circuit each time.

This is inconsistent when Noir "knows" about the circuit you're interacting with when you're proving/verifying but not for execution.

Comment on lines +26 to +27
const solvedWitnessString = await new Noir(assert_lt_json).execute(inputsString, assert_lt_program);
const solvedWitnessNumber = await new Noir(assert_lt_json).execute(inputsNumber, assert_lt_program);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are broken. You're passing the circuit in twice.

@kobyhallx
Copy link
Contributor

kobyhallx commented Oct 11, 2023

I have similar observations on the surface to Tom. Seems that we do not need noir_js which by design should represent noir program which can be processed with initialised backend. With proposed changes it feels that we would rather benefit from backend package that would be used by developer while taking artefacts from noir (or hide them within it's API)?
Having said that, are we able to define a complete API for all backends?

@signorecello
Copy link
Contributor Author

So I've talked with @TomAFrench and it seems like we could drop this in favour of lazy initialization of the CRS which would be a more dynamic way of achieving the same (not passing the circuit to both instances).

I'll be spinning off of this to add the destroy method and the options object in another PR.

Thanks @kobyhallx for the review

@signorecello signorecello deleted the zpedro/noirjs branch October 11, 2023 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants