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

RFC-0003: Asynchronous Circuits in o1js #4

Merged
merged 1 commit into from
Feb 28, 2024

Conversation

teddyjfpender
Copy link
Contributor

@teddyjfpender teddyjfpender commented Nov 27, 2023

🚀 Introduction to the Innovation

Here is RFC-0003! It's about bringing asynchronous circuits to o1js. This enhancement is set to significantly improve zkApp design patterns and eliminate work-arounds for performing asynchronous operations with circuits.

🔑 Why This Change Is Crucial

Currently, o1js circuits are constrained by sequential execution and are unable to perform asynchronous operations, which limits their ability to interact with dynamic data sources (such as other circuits!). By adding asynchronous circuit capabilities, we can overcome these limitations, offering a boost to zkApp design flexibility.

🔍 Exploring Practical Use Cases

There are a few detailed scenarios like dynamic data integration in smart contract circuits and real-time player attribute updates in games that touch on a small part of the large impact this feature can have across a variety of applications.

💭 Seeking Your Input

Your expertise, implementation designs, feedback, and creative use-case scenarios are invaluable in shaping a robust implementation.

📖 For More Details

Please refer to the full RFC document for an in-depth understanding of the scope, potential impact, and the challenges addressed.

🤝 Join the Discussion

This is an open invitation to all developers, users, and stakeholders within the Mina ecosystem to be a part of this exciting development. Let's work together to take the Mina ecosystem to new heights of innovation and functionality!

@teddyjfpender teddyjfpender requested a review from a team as a code owner November 27, 2023 20:16
@mitschabaude
Copy link

mitschabaude commented Jan 11, 2024

Some notes on implementation:

An o1js circuit is given as a callback to Pickles, which gives it as a callback to Snarky at various steps of compiling/proving. In both Pickles and Snarky, the circuit is expected to finish synchronously. Therefore, the main lift is to adapt both of those libraries to allow async circuits.

We already had to allow dealing with JS Promises in other places of Pickles (like when it waits for a Kimchi proof to finish), therefore there is an OCaml library called Promise (code here) which implements Promises for both native OCaml and JS. The goal would be to make a circuit coming from o1js return a Promise.t, and thread that through Pickles.

A good starting point to enable async circuits is probably the checked_runner.ml module of snarky, particularly the type run which describes how a function 't is run to produce a value 'a, and in the process update snarky's internal state of type run_state:

https://github.com/o1-labs/snarky/blob/94b2df82129658d505b612806a5804bc192f13f0/src/base/checked_runner.ml#L433

(* checked_runner.ml *)
type ('a, 't) run = 't -> run_state -> run_state * 'a

In the new model, we instead want that type to return (run_state * 'a) Promise.t. An interesting exercise, also to get to know snarky, is to just change the type and then start fixing everything that breaks:

(* checked_runner.ml *)
- type ('a, 't) run = 't -> run_state -> run_state * 'a
+ type ('a, 't) run = 't -> run_state -> (run_state * 'a) Promise.t

To make this work, you need to add the promise library to snarky's dune file

 (libraries bitstring_lib core_kernel h_list interval_union snarky_intf
-  bignum.bigint)
+ promise bignum.bigint)

For dealing with promises, you could use monadic let syntax, like this:

(* example code using a `run` of type `run` *)
- let state, result =  run circuit state in
+ let%map.Promise state, result =  run circuit state in

which magically will make the function this is done in return a Promise.t.

However, instead of literally changing the run type and fixing everything that breaks, a smoother approach would be to introduce a sibling type, run_async, and progressively use that in more and more places:

(* checked_runner.ml *)
type ('a, 't) run = 't -> run_state -> run_state * 'a
+ type ('a, 't) run_async = 't -> run_state -> (run_state * 'a) Promise.t

This would be my preferred approach to start personally!

Matthew also suggested functoring over the run type entirely, so different consumers of snarky could make it be either async or sync. This would have the immense benefit that not all code in snarky using run has to be repeated in a run_async version.

@mitschabaude
Copy link

Implementation is under way! o1-labs/o1js#1450

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants