Skyward reduces compilation time of a O1JS ZK program on Web.
Skyward allows you to pre-cache artifacts like prover keys, verification keys, SRS, and so on. With that ZK program compilation takes just a few seconds.
Check out the demo at https://skyward.run. The code for it is in examples/example-vite folder.
You could see pre-cached compilation is 2-5x faster, on mobile and desktop. After you refresh the page, the improvement is about 15x on mobile, which is considerable for a repeat user. More complex ZK program you have, more the difference is.
Add skyward
as a dependency using npm
, pnpm
or yarn
:
npm add skyward
Before the artifacts can be used for pre-caching, they have to be made available on the Web.
Create a script that prepares the artifacts. Let's say you compile HelloProgram
:
// scripts/compile.ts
import { prepare } from "skyward/prepare";
import { HelloProgram } from "../src/hello-program.js";
await prepare(HelloProgram);
Run it, for example using tsx:
tsx scripts/compile.ts
That would make the artifacts known for skyward
. You could see that if you list the known programs:
npx skyward list
For example-vite
and HelloProgram
it outputs:
┌───────────────┬──────────────────────────────────────────────┬───────┬──────────┐
│ Name │ Hash │ Files │ Size │
├───────────────┼──────────────────────────────────────────────┼───────┼──────────┤
│ hello-program │ uJS7R9Qnl78lcuvLU7yVsZaAVYD_otIMyBjj8G2XV42I │ 10 │ 15.97 MB │
└───────────────┴──────────────────────────────────────────────┴───────┴──────────┘
You should create an account to upload the artifacts on skyward cloud. See Cloud section for rationale.
npx skyward login
After the artifacts are uploaded, skyward will create a JSON manifest file skyward.json
.
It is used to pre-cache all the necessary artifacts for a program when in browser.
We expect the file to be located in src/data/skyward.json
. We pass the folder to the command:
npx skyward upload src/data
If you have multiple programs, and you intend to upload just specific programs:
npx skyward upload src/data -p program1 -p program2
Now you should tell O1JS to use a pre-populated cache. Let's assume you have a vanilla code like this:
import { HelloProgram } from "../hello-program.js";
import { expose } from "comlink";
const functions = {
async compile() {
await HelloProgram.compile();
},
};
expose(functions);
It should be changed to look like this:
import { HelloProgram } from "../hello-program.js";
import { expose } from "comlink";
import { RemoteCache, decode, ManifestContent } from "skyward/browser";
import * as manifestJSON from "../data/skyward.json";
// Instantiate a cache from the manifest file
const cache = RemoteCache.fromManifest(decode(ManifestContent, manifestJSON));
// Promise that resolves when we are done pre-caching the artifacts
const precacheP = cache.populate(HelloProgram.name);
const functions = {
async compile() {
await precacheP;
await HelloProgram.compile({ cache: cache });
},
};
expose(functions);
See, we pass our custom cache
to HelloProgram.compile
.
The cache gets pre-populated by the artifacts listed in manifestJSON
you created on a previous step.
To ensure it has all the artifacts, we wait (await precacheP
) until the pre-caching is done.
You could host the artifacts along with the rest of your webapp bundle. It means though, you have to build them before the bundling, or store them in a repository. Or, you could use your own blob storage - Amazon S3, GCP Storage, Vercel Blobs, etc. We found it to be quite inconvenient: manage automation, proper caching, getting big repos, manage HTTP headers. We decided to make the experience more comfortable.
You could see all the commands available running
npx skyward --help
adduser
- Add a user account, asks for email and passwordlogin
- Login to the account, asks for email and passwordlogout
- Clear locally stored access token for the logged in userwhoami
- Show an email of a user currently logged inlist
- List known ZK programs of the projectupload
- Upload ZK program artifacts to the cloudserve
- Serve program artifacts over HTTP locally, uses port 4040 by defaultextract
- Similar to upload, but copies the artifacts to the target folder
The last two commands are worth describing in more detail.
serve
: During local development, you might want to avoid cloud. You might consume locally-served artifacts.
We could adapt our familiar HelloProgram
example by changing cache
variable:
// Instantiate a cache from the manifest file served at http://localhost:4040/skyward.json
// This is default by `skyward serve`
const cache = RemoteCache.fromManifestURL();
extract
: Useful in case you want to host the artifacts by yourself. It generates a manifest JSON file same as upload
,
and copies the artifacts to the destination folder of your choosing.
npx skyward extract src/data
That would create the artifacts at src/data
folder, and src/data/skyward.json
manifest file that references those.
The manifest file could be used as shown above.
Feel free to dive in! Open an issue, submit PRs, ask questions, make suggestions.
MIT OR Apache-2.0