/starlight

:zap: solidity --> zApp transpiler :zap:

Primary LanguageJavaScriptOtherNOASSERTION

starlight 🌠

Generate a zApp from a Solidity contract.


Introduction

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 an enormously detailed explanation of how the transpiler works.


Warnings

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.



Requirements

To run the zappify command:

  • Node.js v15 or higher.
    (Known issues with v13).

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.

Quick User Guide

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!


Install

Whilst the package is in early development, it isn't hosted on npm. To install:

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.

Run

zappify -i ./path/to/MyZolidityContract.zol

... converts a Zolidity contract into a zApp. By default, the zApp is output to a ./zapps/ folder.

CLI options

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.
--help -h CLI help.

Troubleshooting

Installation

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

Compilation

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.


Developer

Testing

full zapp

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!

Finally, run a test, which executes the function privately, using some test parameters:

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.

circuit

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

Contributing

See here for our contribution guidelines.


License

CC0 1.0 Universal

Notes:


Acknowledgements

Authors:

  • MirandaWood
  • iAmMichaelConnor

Important packages:

Inspirational works: