2023-04-08 19:17:52 +00:00
// crypto types
2023-04-27 17:34:22 +00:00
import { InputFor , OutputOf , ZKDepositData } from 'types/sdk/crypto'
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-04-23 22:01:45 +00:00
import { ErrorUtils , HexUtils , NumberUtils } from 'lib/utils'
2023-04-08 19:17:52 +00:00
// Parse some files
2023-04-23 22:01:45 +00:00
import { Files , Json } from 'lib/data'
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-04-11 19:36:32 +00:00
export async function getProvingKey ( ) : Promise < ArrayBufferLike > {
2023-04-08 19:17:52 +00:00
return ( await Files . loadRaw ( 'circuits/tornadoProvingKey.bin' ) ) . buffer
}
2023-04-23 22:01:45 +00:00
export async function getTornadoCircuit ( ) : Promise < any > {
return Json . load ( '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
}
}
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 ( )
const circuit = await Setup . getTornadoCircuit ( )
const provingKey = await Setup . getProvingKey ( )
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. 🤷♀️
groth16 . terminate ( )
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
}
// TODO: implement and decide whether to add in declarations an ambient namespace and merge it here
// export function buildMerkleTree(deposit: Crypto.TornadoDeposit): Crypto.MerkleTree {}
// export function calcMerkleProof(tree: Crypto.MerkleTree): Crypto.MerkleProof {}
// export function calcDepositProof(merkleProof: Crypto.InputFor.DepositProof): Crypto.OutputOf.DepositProof {}
// Namespace exports
2023-04-27 17:34:22 +00:00
export { InputFor , OutputOf , ZKDepositData }