2023-04-08 19:17:52 +00:00
// External crypto
import circomlib from 'circomlib'
2023-04-11 19:36:32 +00:00
import { bigInt } from 'snarkjs'
import { Groth16 } from 'websnark/src/groth16'
2023-04-08 19:17:52 +00:00
import { buildGroth16 } from 'websnark'
2023-04-23 22:01:45 +00:00
import MerkleTreeDefault , { MerkleTree } from 'fixed-merkle-tree'
2023-04-11 19:36:32 +00:00
import { genWitnessAndProve , toSolidityInput } from 'websnark/src/utils'
2023-04-08 19:17:52 +00:00
// Some utils to work with hex numbers
2023-05-02 22:19:28 +00:00
import { ErrorUtils , HexUtils , NumberUtils } from '@tornado/sdk-utils'
2023-04-08 19:17:52 +00:00
// Parse some files
2023-05-02 22:19:28 +00:00
import { Files , Json } from '@tornado/sdk-data'
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECLARATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Interfaces for the cryptographic primitives.
export type bigInt = typeof bigInt
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OUTPUTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ * *
* These are types which define and scope ( ! ) the outputs of the cryptograhic
* functions . They have been scoped such that there isn ' t much naming confusion
* between what a function takes and not . Common aliases are used there where deemed
* appropriate .
* /
export namespace OutputOf {
export type PedersenHash = bigInt
export interface CreateDeposit {
nullifier : bigInt
secret : bigInt
preimage : Buffer
commitment : PedersenHash
hexCommitment : string
nullifierHash : PedersenHash
hexNullifierHash : string
}
export interface MerkleProof {
root : any
path : {
indices : any
elements : any [ ]
}
}
export interface Groth16Proof {
pi_a : string
pi_b : string
pi_c : string
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OUTPUT ALIASES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ * *
* There may be also multiple output aliases for one output , to make the code more readable ,
* in a certain sense of speaking . The delimiter below just makes this comment not pass on to
* ` MerkleTree ` .
* /
type __OutputAliasDelimiter = null
export type MerkleProof = OutputOf . MerkleProof
export type ZKProof = OutputOf . Groth16Proof
export type DepositProof = OutputOf . Groth16Proof
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INPUTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ * *
* These are types which define and scope ( ! ) the inputs of the cryptograhic
* functions . The same as above applies , but note that some outputs are inputs
* for other functions . For example , MerkleTree is an output of the function
* which builds the Tornado Cash merkle tree , and this is passed as input ,
* to the merkle proof builder .
* /
export namespace InputFor {
export interface PedersenHash {
msg : Buffer
}
export interface CreateDeposit {
nullifier? : bigInt
secret? : bigInt
}
export interface BuildMerkleTree {
height : number
leaves : Array < string >
}
export interface ZKProof {
public : {
root : string
tree : MerkleTree
leafIndex : number
hexNullifierHash : string
recipientAddress : string
relayerAddress : string
fee : bigInt
refund : bigInt
}
private : {
nullifier : bigInt
secret : bigInt
}
}
interface PublicGroth16 {
root : any
nullifierHash : PedersenHash
recipient : bigInt
relayer : bigInt
refund : bigInt
fee : bigInt
}
interface PrivateGroth16 {
nullifier : bigInt
secret : bigInt
pathIndices : number [ ]
pathElements : string [ ]
}
export type Groth16 = PublicGroth16 & PrivateGroth16
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INPUT ALIASES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ * *
* There may be also multiple input aliases for one input , to make the code more readable ,
* in a certain sense of speaking . The delimiter below just makes this comment not pass on to
* ` TornadoDeposit ` .
* /
type __InputAliasDelimiter = null
export type ZKDepositData = OutputOf . CreateDeposit
2023-04-08 19:17:52 +00:00
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETUP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ * *
* Several objects have to be set up , like the groth16 prover . All related types are
* ( will be ) contained within this namespace .
* /
export namespace Setup {
2023-05-06 23:59:38 +00:00
export function getProvingKey ( ) : ArrayBufferLike {
return Files . loadRawSync ( 'circuits/tornadoProvingKey.bin' ) . buffer
2023-04-08 19:17:52 +00:00
}
2023-05-06 23:59:38 +00:00
export function getTornadoCircuit ( ) : any {
return Json . loadSync ( 'circuits/tornado.json' )
2023-04-08 19:17:52 +00:00
}
2023-04-27 17:34:22 +00:00
let cachedGroth16Prover : Groth16 | null = null
2023-04-11 19:36:32 +00:00
/ * *
* @note The following is a comment from tornado - cli : ` groth16 initialises a lot of Promises that will never be resolved, that's why we need to use process.exit to terminate the CLI ` . They literally didn ' t check the code to see that these are just worker threads and that ` groth16 ` has a ` terminate() ` function to remove them . 🤦
* /
export async function getGroth16 ( ) : Promise < Groth16 > {
2023-04-08 19:17:52 +00:00
const defaultParams = { wasmInitialMemory : 5000 }
2023-04-27 17:34:22 +00:00
if ( ! cachedGroth16Prover ) cachedGroth16Prover = await buildGroth16 ( defaultParams )
return cachedGroth16Prover
2023-04-08 19:17:52 +00:00
}
2023-05-06 23:59:38 +00:00
export function terminateGroth16 ( ) : void {
cachedGroth16Prover ! . terminate ( )
cachedGroth16Prover = null
}
2023-04-08 19:17:52 +00:00
}
export namespace Primitives {
2023-04-27 17:34:22 +00:00
export function calcPedersenHash ( pedersenHashData : InputFor.PedersenHash ) : OutputOf . PedersenHash {
2023-04-08 19:17:52 +00:00
return circomlib . babyJub . unpackPoint ( circomlib . pedersenHash . hash ( pedersenHashData . msg ) ) [ 0 ]
}
export function createNote ( msg : Buffer ) : string {
return HexUtils . bufferToHex ( msg , 62 )
}
2023-04-27 17:34:22 +00:00
export function parseNote ( hexNote : string ) : ZKDepositData {
2023-04-17 21:56:57 +00:00
const _hexNote = hexNote . split ( '_' ) [ 1 ] ? ? hexNote
2023-04-23 22:01:45 +00:00
const buffer = Buffer . from ( _hexNote . slice ( 2 ) , 'hex' )
2023-04-11 19:36:32 +00:00
return createDeposit ( {
// @ts-expect-error
nullifier : bigInt.leBuff2int ( buffer . subarray ( 0 , 31 ) ) ,
// @ts-expect-error
2023-04-23 22:01:45 +00:00
secret : bigInt.leBuff2int ( buffer . subarray ( 31 , 62 ) )
2023-04-11 19:36:32 +00:00
} )
}
2023-04-23 22:01:45 +00:00
export function createDeposit (
2023-04-27 17:34:22 +00:00
input : InputFor.CreateDeposit = {
2023-04-23 22:01:45 +00:00
nullifier : NumberUtils.randomBigInteger ( 31 ) ,
secret : NumberUtils.randomBigInteger ( 31 )
}
2023-04-27 17:34:22 +00:00
) : ZKDepositData {
2023-04-11 19:36:32 +00:00
// @ts-expect-error
2023-04-17 21:56:57 +00:00
let preimage = Buffer . concat ( [ input . nullifier . leInt2Buff ( 31 ) , input . secret . leInt2Buff ( 31 ) ] )
2023-04-08 19:17:52 +00:00
let commitment = calcPedersenHash ( { msg : preimage } )
let commitmentHex = HexUtils . bigIntToHex ( commitment )
2023-04-11 19:36:32 +00:00
// @ts-expect-error
2023-04-17 21:56:57 +00:00
let nullifierHash = calcPedersenHash ( { msg : input.nullifier.leInt2Buff ( 31 ) } )
2023-04-08 19:17:52 +00:00
let nullifierHex = HexUtils . bigIntToHex ( nullifierHash )
return {
2023-04-11 19:36:32 +00:00
nullifier : input.nullifier ! ,
secret : input.secret ! ,
2023-04-08 19:17:52 +00:00
preimage : preimage ,
commitment : commitment ,
2023-04-11 19:36:32 +00:00
hexCommitment : commitmentHex ,
2023-04-08 19:17:52 +00:00
nullifierHash : nullifierHash ,
2023-04-11 19:36:32 +00:00
hexNullifierHash : nullifierHex
2023-04-08 19:17:52 +00:00
}
}
2023-04-11 19:36:32 +00:00
2023-04-27 17:34:22 +00:00
export function buildMerkleTree ( inputs : InputFor.BuildMerkleTree ) : MerkleTree {
2023-04-23 22:01:45 +00:00
// @ts-expect-error
return new MerkleTreeDefault ( inputs . height , inputs . leaves )
2023-04-11 19:36:32 +00:00
}
2023-04-27 17:34:22 +00:00
export async function calcDepositProofs ( inputs : Array < InputFor.ZKProof > ) : Promise < Array < Array < string > >> {
2023-04-11 19:36:32 +00:00
const proofs : string [ ] [ ] = [ ]
const groth16 = await Setup . getGroth16 ( )
2023-05-06 23:59:38 +00:00
const circuit = Setup . getTornadoCircuit ( )
const provingKey = Setup . getProvingKey ( )
2023-04-11 19:36:32 +00:00
for ( let i = 0 , len = inputs . length ; i < len ; i ++ ) {
const input = inputs [ i ]
2023-04-23 22:01:45 +00:00
// TODO: remove try and const again after fixing
let proofData
2023-04-11 19:36:32 +00:00
// Compute Merkle Proof
2023-04-23 22:01:45 +00:00
// The ts return is noted as `pathIndex` but using this we get an undefined because it is really `pathIndices`???
// TODO: Bug that needs to be fixed (above)
// @ts-expect-error
const { pathElements , pathIndices } = input . public . tree . path ( input . public . leafIndex )
2023-04-11 19:36:32 +00:00
proofs . push ( [ ] )
2023-04-23 22:01:45 +00:00
try {
proofData = await genWitnessAndProve (
groth16 ,
{
// Public inputs
root : input.public.root ,
// @ts-expect-error
nullifierHash : bigInt ( input . public . hexNullifierHash ) ,
// @ts-expect-error
recipient : bigInt ( input . public . recipientAddress ) ,
// @ts-expect-error
relayer : bigInt ( input . public . relayerAddress ) ,
// @ts-expect-error
fee : bigInt ( input . public . fee ) ,
//
refund : input.public.refund ,
// Private inputs
nullifier : input.private.nullifier ,
secret : input.private.secret ,
pathElements : pathElements ,
pathIndices : pathIndices
} ,
circuit ,
provingKey
)
} catch ( err ) {
groth16 . terminate ( )
throw ErrorUtils . ensureError ( err )
}
2023-04-11 19:36:32 +00:00
proofs [ i ] . push ( toSolidityInput ( proofData ) . proof )
2023-04-17 21:56:57 +00:00
proofs [ i ] . push (
2023-04-11 19:36:32 +00:00
input . public . root ,
input . public . hexNullifierHash ,
2023-04-17 21:56:57 +00:00
'0x' + HexUtils . prepareAddress ( input . public . recipientAddress , 20 ) ,
'0x' + HexUtils . prepareAddress ( input . public . relayerAddress , 20 ) ,
HexUtils . bigIntToHex ( input . public . fee ) ,
HexUtils . bigIntToHex ( input . public . refund )
)
2023-04-11 19:36:32 +00:00
}
// Done. 🤷♀️
2023-05-06 23:59:38 +00:00
Setup . terminateGroth16 ( )
2023-04-11 19:36:32 +00:00
2023-04-17 21:56:57 +00:00
return proofs
2023-04-11 19:36:32 +00:00
}
2023-04-08 19:17:52 +00:00
}