This code is not owned by EY and EY provides no warranty and disclaims any and all liability for use of this code. Users must conduct their own diligence with respect to use for their purposes and any and all usage is on an as-is basis and at your own risk.
Generate a zApp from a Solidity contract.
zApps are zero-knowledge applications. They're like dApps (decentralised applications), but with privacy. zApps are tricky to write, but Solidity contracts are lovely to write. So why not try to write a zApp with Solidity?
starlight
helps developers do just this...
- Write a Solidity contract
- Add a few new privacy decorators to the contract (to get a 'Zolidity' contract)
- Run
zappify
- Get a fully working zApp in return
Solidity contract --> Zolidity contract --> zappify --> zApp
The main objective of this transpiler is to enable developers to quickly draft frameworks for zApps.
See here for the gitbook, and here for an enormously detailed explanation of how the transpiler works.
This code is not owned by EY and EY provides no warranty and disclaims any and all liability for use of this code. Users must conduct their own diligence with respect to use for their purposes and any and all usage is on an as-is basis and at your own risk.
Note that this is an experimental prototype which is still under development. Not all Solidity syntax is currently supported. Here is guide to current functionality.
This code has not yet completed a security review and therefore we strongly recommend that you do not use it in production. We take no responsibility for any loss you may incur through the use of this code.
Due to its experimental nature, we strongly recommend that you do not use any zApps which are generated by this transpiler to transact items of material value.
Output zApps use the proof system Groth16 which is known to have vulnerabilities. Check if you can protect against them, or are protected by the zApps logic.
In the same way that Solidity does not protect against contract vulnerabilities, Starlight cannot protect against zApp vulnerabilities once they are written into the code. This compiler assumes knowledge of smart contract security.
- Quick User Guide
- Install via npm
- Install via clone
- Run
- Troubleshooting
- Developer
- License
- Acknowledgements
To run the zappify
command:
- Node.js v15 or higher - v16 recommended.
(Known issues with v13 and v18).
To run the resulting zApp:
- Node.js v15 or higher.
- Docker (with 16GB RAM recommended)
- Mac and Linux machines with at least 16GB of memory and 10GB of disk space are supported.
Take a 'normal' smart contract, like this one:
// SPDX-License-Identifier: CC0
pragma solidity ^0.8.0;
contract Assign {
uint256 private a;
function assign(uint256 value) public {
a = value;
}
}
Then add secret
in front of each declaration you want to keep secret:
// SPDX-License-Identifier: CC0
pragma solidity ^0.8.0;
contract Assign {
secret uint256 private a; // <--- secret
function assign(secret uint256 value) public { // <--- secret
a = value;
}
}
Save this decorated file with a .zol
extension ('zolidity').
Run zappify -i <./path/to/file>.zol
and get an entire standalone zApp in return!
Starlight is now available on github's npm package repository!
To install, you need to make sure that npm
is looking in that registry, not the default npmjs
:
npm config set @eyblockchain:registry https://npm.pkg.github.com
<-- this sets any @eyblockchain
packages to be installed from the github registry
npm i @eyblockchain/starlight@<latest-version>
Then you can import the zappify
command like so:
import { zappify } from "@eyblockchain/starlight";
The only required input for zappify
is the file path of the .zol
file you'd like to compile, e.g. zappify('./contracts/myContract.zol')
. You can optionally add a specific zApp name and the output directory:
zappify('./contracts/myContract.zol', 'myzApp', './test_zApps')
Then it works just as described in the below section Run.
Since we push the npm package to github's repository, you may need to specify the package location in your project's .npmrc
file and authenticate to github. Try adding something like:
@eyblockchain:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=<your_github_token>
...and re-run the install command, or manually add "@eyblockchain/starlight"
to your package.json
.
To install via github:
Clone the repo.
cd starlight
./bin/start
(You might need to run chmod +x ./bin/start for permission to execute the newly created shell scripts)
This runs tsc
and npm i -g ./
which will create a symlink to your node.js bin, allowing you to run the commands specified in the "bin":
field of the package.json
; namely the zappify
command.
zappify -i ./path/to/MyZolidityContract.zol
... converts a Zolidity contract into a zApp. By default, the zApp is output to a ./zapps/
folder.
option | abbr. | description |
---|---|---|
--input <./path/to/contract.zol> |
-i |
Specify an input contract file with a .zol extension. |
--output <./custom/output/dir/> |
-o |
Specify an output directory for the zApp. By default, the zApp is output to a ./zapps/ folder. |
--zapp-name <customZappName> |
-z |
Otherwise files get output to a folder with name matching that of the input file. |
--log-level <debug> |
- | Specify a Winston log level type. |
--enc |
-e |
Add secret state encryption events for every new commitment*. |
--help |
-h |
CLI help. |
*Note that encrypting and broadcasting secret state information to its owner is costly gas-wise, so switching it on with this option may give you stack too deep
errors in the contract.
An alternative is to use our encrypt
syntax in front of the state edit you'd like to broadcast, like this:
contract Assign {
secret uint256 private a; // <--- secret
function assign(secret uint256 value) public { // <--- secret
encrypt a = value; // <--- the new value of a will be encrypted and sent to a's owner
}
}
Partitioned states which are incremented and have an owner which is clearly different from the function caller has encryption included by default.
If the zappify
command isn't working, try the Install steps again. You might need to try npm i --force -g ./
.
In very rare cases, you might need to navigate to your node.js innards and delete zappify from the bin
and lib/node_modules
.
To find where your npm lib is, type npm
and it will tell you the path.
E.g.:
~/.nvm/versions/node/v15.0.1/lib/node_modules/npm
^
lib
^
bin is also at this level
If you find errors to do with 'knownness' or 'unknownness', try to mark incrementations. If you have any secret states which can be incremented by other users, mark those incrementations as unknown
(since the value may be unknown to the caller).
// SPDX-License-Identifier: CC0
pragma solidity ^0.8.0;
contract Assign {
secret uint256 private a; // <--- secret
function add(secret uint256 value) public { // <--- secret
unknown a += value; // <--- may be unknown to the caller
}
function remove(secret uint256 value) public { // <--- secret
a -= value;
}
}
However, if you want the incrementation to only be completed by the secret owner, mark it as known:
// SPDX-License-Identifier: CC0
pragma solidity ^0.8.0;
contract Assign {
secret uint256 private a; // <--- secret
function add(secret uint256 value) public { // <--- secret
known a += value; // <--- must be known to the caller
}
function remove(secret uint256 value) public { // <--- secret
a -= value;
}
}
Failing to mark incrementations will throw an error, because the transpiler won't know what to do with the state. See the write up for more details.
If your input contract has any external imports, make sure those are stored in the ./contracts
directory (in root) and compile with solc
0.8.0.
To test an entire zApp, which has been output by the transpiler:
Having already run zappify
, the newly-created zApp will be located in the output dir you specified (or in a dir called ./zapps
, by default). Step into that directory:
cd zapps/MyContract/
Install dependencies:
npm install
Start docker.
(At this stage, you might need to run chmod +x ./bin/setup && chmod +x ./bin/startup
for permission to execute the newly created shell scripts)
Run trusted setups on all circuit files:
./bin/setup
<-- this can take quite a while!
After this command, you're ready to use the zApp. You can either run a Starlight-generated test, or call the included APIs for each function.
For the compiled test see below:
npm test
<-- you may need to edit the test file (zapps/MyContract/orchestration/test.mjs
) with appropriate parameters before running!
npm run retest
<-- for any subsequent test runs (if you'd like to run the test from scratch, follow the below instructions)
It's impossible for a transpiler to tell which order functions must be called in, or the range of inputs that will work. Don't worry - If you know how to test the input Zolidity contract, you'll know how to test the zApp. The signatures of the original functions are the same as the output nodejs functions. There are instructions in the output test.mjs
on how to edit it.
All the above use Docker in the background. If you'd like to see the Docker logging, run docker-compose -f docker-compose.zapp.yml up
in another window before running.
NB: rerunning npm test
will not work, as the test script restarts the containers to ensure it runs an initialisation, removing the relevant dbs. If you'd like to rerun it from scratch, down the containers with docker-compose -f docker-compose.zapp.yml down -v --remove-orphans
and delete the file zapps/myContract/orchestration/common/db/preimage.json
before rerunning npm test
.
For using APIs:
npm start
<-- this starts up the express
app and exposes the APIs
Each route corresponds to a function in your original contract. Using the Assign.zol
example above, the output zApp would have an endpoint for add
and remove
. By default, requests can be POSTed to http://localhost:3000/<function-name>
in JSON format. Each variable should be named like the original parameter. For example, if you wanted to call add
with the value 10, send:
{
value: 10
}
to http://localhost:3000/add
. This would (privately) increase the value of secret state a
by 10, and return the shield contract's transaction.
To access your secret data locally, you can look at all the commitments you have by sending a GET request to http://localhost:3000/getAllCommitments
.
You can also filter these commitments by variable name. Using the example above, if you wanted to see all commitments you have representing the variable a
, send:
{
"name": "a"
}
as a GET request to http://localhost:3000/getCommitmentsByVariableName
.
If the commitment database that stores commitment preimages is lost you can restore the DB (by decrypting the onchain encryptions of the preimages) by sending a POST request to http://localhost:3000/backupDataRetriever
. You can also restore only the commitments with a specific variable name. For example, if you want to restore commitments representing variable a
, send:
{
"name": "a"
}
as a POST request to http://localhost:3000/backupVariable
.
Starlight handles secret initiation in the constructor by creating a proof at the setup stage. Any user supplied inputs will be prompted for in the command line when running ./bin/setup
.
Since this inevitably creates a commitment to be sent your local db, simply restarting the zapp will not work. The blockchain will be aware of the constructor commitment, but your zapp will not.
If you would like to restart the zapp and redeploy the contract, always (with no running docker containers) run:
./bin/redeploy
<-- creates a new constructor proof and saves the commitment, then deploys the shield contract
npm run restart
<-- if you'd like to use the APIs, always restart rather than start since the latter clears your local dbs
Then, if you previously had nullifiers, reinstate them in your local sparse merkle tree by sending a POST to http://localhost:3000/reinstateNullifiers
.
Apart from local ganache instance, Starlight output zapps can now be deployed in Sepolia, Goerli and Polygon Mumbai as cli options. Connection to Sepolia and Goerli are made through infura endpoints and that of Polygon Mumbai is provided via maticvigil.
The configuration can be done during ./bin/setup
phase in the following way.
./bin/setup -n network -a account -k privatekey -m "12 letter mnemonic" -r APIKey
option | description |
---|---|
-n |
Network : Specify testnet you want to connect to. possible options: mumbai/ sepolia/ goerli |
-a |
Ethereum address |
-k |
Private key of above ethereum address |
-m - |
12 letter mnemonic passphrase |
-r |
API key or APPID of endpoint |
-s |
Zkp setup flag , Default to yes . If you had already created zkp keys before and just want to configure deployment infrastructure, pass -s n |
cd ./path/to/myCircuit.zok
docker run -v $PWD:/app/code -ti docker.pkg.github.com/eyblockchain/zokrates-worker/zokrates_worker:1.0.8 /bin/bash
./zokrates compile --light -i code/myCircuit.zok
<-- it should compile
Starlight uses containerised zokrates from zokrates-worker-starlight. Here we have use two zokrates container, zokrates -version:0.7.12 to compile the circuits and zokrates -version:0.8.1 to do the setup and generate-proof to improve the key and proof generation time.
See here for our contribution guidelines.
Notes:
- This repository contains some modules of code and portions of code from Babel (https://github.com/babel/babel). All such code has been modified for use in this repository. Babel is MIT licensed. See LICENSE file.
- This repository contains some portions of code from The Super Tiny Compiler (https://github.com/jamiebuilds/the-super-tiny-compiler). All such portions of code have been modified for use in this repository. The Super Tiny Compiler is CC-BY-4.0 licensed. See LICENSE file.
Authors:
- MirandaWood
- iAmMichaelConnor
Important packages:
Inspirational works: