From e39b5ef69468ce50c7e9e72e5e4d67c577909b24 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Fri, 10 May 2024 22:38:35 +0300 Subject: [PATCH] import circom helpers --- benchmarks/circom_ark_prover_cli.nim | 218 +++++++++++++++++++++++---- 1 file changed, 190 insertions(+), 28 deletions(-) diff --git a/benchmarks/circom_ark_prover_cli.nim b/benchmarks/circom_ark_prover_cli.nim index 049e9b9b..737056fd 100644 --- a/benchmarks/circom_ark_prover_cli.nim +++ b/benchmarks/circom_ark_prover_cli.nim @@ -20,6 +20,195 @@ type CircuitFiles* = object inputs*: string dir*: string circName*: string + backendCfg : ptr CircomBn254Cfg + vkp* : ptr VerifyingKey + +proc release*(self: CircomCompat) = + ## Release the ctx + ## + + if not isNil(self.backendCfg): + self.backendCfg.unsafeAddr.releaseCfg() + + if not isNil(self.vkp): + self.vkp.unsafeAddr.release_key() + +proc prove*[H]( + self: CircomCompat, + input: ProofInputs[H]): ?!CircomProof = + ## Encode buffers using a ctx + ## + + # NOTE: All inputs are statically sized per circuit + # and adjusted accordingly right before being passed + # to the circom ffi - `setLen` is used to adjust the + # sequence length to the correct size which also 0 pads + # to the correct length + doAssert input.samples.len == self.numSamples, + "Number of samples does not match" + + doAssert input.slotProof.len <= self.datasetDepth, + "Number of slot proofs does not match" + + doAssert input.samples.allIt( + block: + (it.merklePaths.len <= self.slotDepth + self.blkDepth and + it.cellData.len <= self.cellElms * 32)), "Merkle paths length does not match" + + # TODO: All parameters should match circom's static parametter + var + ctx: ptr CircomCompatCtx + + defer: + if ctx != nil: + ctx.addr.releaseCircomCompat() + + if initCircomCompat( + self.backendCfg, + addr ctx) != ERR_OK or ctx == nil: + raiseAssert("failed to initialize CircomCompat ctx") + + var + entropy = input.entropy.toBytes + dataSetRoot = input.datasetRoot.toBytes + slotRoot = input.slotRoot.toBytes + + if ctx.pushInputU256Array( + "entropy".cstring, entropy[0].addr, entropy.len.uint32) != ERR_OK: + return failure("Failed to push entropy") + + if ctx.pushInputU256Array( + "dataSetRoot".cstring, dataSetRoot[0].addr, dataSetRoot.len.uint32) != ERR_OK: + return failure("Failed to push data set root") + + if ctx.pushInputU256Array( + "slotRoot".cstring, slotRoot[0].addr, slotRoot.len.uint32) != ERR_OK: + return failure("Failed to push data set root") + + if ctx.pushInputU32( + "nCellsPerSlot".cstring, input.nCellsPerSlot.uint32) != ERR_OK: + return failure("Failed to push nCellsPerSlot") + + if ctx.pushInputU32( + "nSlotsPerDataSet".cstring, input.nSlotsPerDataSet.uint32) != ERR_OK: + return failure("Failed to push nSlotsPerDataSet") + + if ctx.pushInputU32( + "slotIndex".cstring, input.slotIndex.uint32) != ERR_OK: + return failure("Failed to push slotIndex") + + var + slotProof = input.slotProof.mapIt( it.toBytes ).concat + + slotProof.setLen(self.datasetDepth) # zero pad inputs to correct size + + # arrays are always flattened + if ctx.pushInputU256Array( + "slotProof".cstring, + slotProof[0].addr, + uint (slotProof[0].len * slotProof.len)) != ERR_OK: + return failure("Failed to push slot proof") + + for s in input.samples: + var + merklePaths = s.merklePaths.mapIt( it.toBytes ) + data = s.cellData + + merklePaths.setLen(self.slotDepth) # zero pad inputs to correct size + if ctx.pushInputU256Array( + "merklePaths".cstring, + merklePaths[0].addr, + uint (merklePaths[0].len * merklePaths.len)) != ERR_OK: + return failure("Failed to push merkle paths") + + data.setLen(self.cellElms * 32) # zero pad inputs to correct size + if ctx.pushInputU256Array( + "cellData".cstring, + data[0].addr, + data.len.uint) != ERR_OK: + return failure("Failed to push cell data") + + var + proofPtr: ptr Proof = nil + + let proof = + try: + if ( + let res = self.backendCfg.proveCircuit(ctx, proofPtr.addr); + res != ERR_OK) or + proofPtr == nil: + return failure("Failed to prove - err code: " & $res) + + proofPtr[] + finally: + if proofPtr != nil: + proofPtr.addr.releaseProof() + + success proof + +proc verify*[H]( + self: CircomCompat, + proof: CircomProof, + inputs: ProofInputs[H]): ?!bool = + ## Verify a proof using a ctx + ## + + var + proofPtr = unsafeAddr proof + inputs = inputs.toCircomInputs() + + try: + let res = verifyCircuit(proofPtr, inputs.addr, self.vkp) + if res == ERR_OK: + success true + elif res == ERR_FAILED_TO_VERIFY_PROOF: + success false + else: + failure("Failed to verify proof - err code: " & $res) + finally: + inputs.releaseCircomInputs() + +proc init*( + _: type CircomCompat, + r1csPath : string, + wasmPath : string, + zkeyPath : string = "", + slotDepth = DefaultMaxSlotDepth, + datasetDepth = DefaultMaxDatasetDepth, + blkDepth = DefaultBlockDepth, + cellElms = DefaultCellElms, + numSamples = DefaultSamplesNum): CircomCompat = + ## Create a new ctx + ## + + var cfg: ptr CircomBn254Cfg + var zkey = if zkeyPath.len > 0: zkeyPath.cstring else: nil + + if initCircomConfig( + r1csPath.cstring, + wasmPath.cstring, + zkey, cfg.addr) != ERR_OK or cfg == nil: + if cfg != nil: cfg.addr.releaseCfg() + raiseAssert("failed to initialize circom compat config") + + var + vkpPtr: ptr VerifyingKey = nil + + if cfg.getVerifyingKey(vkpPtr.addr) != ERR_OK or vkpPtr == nil: + if vkpPtr != nil: vkpPtr.addr.releaseKey() + raiseAssert("Failed to get verifying key") + + CircomCompat( + r1csPath : r1csPath, + wasmPath : wasmPath, + zkeyPath : zkeyPath, + slotDepth : slotDepth, + datasetDepth: datasetDepth, + blkDepth : blkDepth, + cellElms : cellElms, + numSamples : numSamples, + backendCfg : cfg, + vkp : vkpPtr) proc runArkCircom( args: CircuitArgs, files: CircuitFiles, proofInputs: ProofInputs[Poseidon2Hash] @@ -47,21 +236,11 @@ proc runArkCircom( proc printHelp() = echo "usage:" - echo " ./codex_ark_prover_cli [options] " + echo " ./circom_ark_prover_cli [options] " echo "" echo "available options:" echo " -h, --help : print this help" echo " -v, --verbose : verbose output (print the actual parameters)" - echo " -d, --depth = : maximum depth of the slot tree (eg. 32)" - echo " -N, --maxslots = : maximum number of slots (eg. 256)" - # echo " -c, --cellsize = : cell size in bytes (eg. 2048)" - # echo " -b, --blocksize = : block size in bytes (eg. 65536)" - echo " -s, --nslots = : number of slots in the dataset (eg. 13)" - echo " -n, --nsamples = : number of samples we prove (eg. 100)" - echo " -e, --entropy = : external randomness (eg. 1234567)" - # echo " -S, --seed = : seed to generate the fake data (eg. 12345)" - echo " -i, --index = : index of the slot (within the dataset) we prove" - echo " -K, --ncells = : number of cells inside this slot (eg. 1024; must be a power of two)" echo "" echo "Must provide files options. Use either:" echo " --dir:$CIRCUIT_DIR --name:$CIRCUIT_NAME" @@ -92,23 +271,6 @@ proc parseCliOptions(args: var CircuitArgs, files: var CircuitFiles) = case key of "h", "help": printHelp() - of "d", "depth": - args.depth = parseInt(value) - of "N", "maxslots": - args.maxslots = parseInt(value) - # of "c", "cellsize" : args.cellsize = checkPowerOfTwo(parseInt(value),"cellSize") - # of "b", "blocksize" : args.blocksize = checkPowerOfTwo(parseInt(value),"blockSize") - of "n", "nsamples": - args.nsamples = parseInt(value) - of "e", "entropy": - args.entropy = parseInt(value) - # of "S", "seed" : args.seed = parseInt(value) - of "s", "nslots": - args.nslots = parseInt(value) - of "K", "ncells": - args.ncells = checkPowerOfTwo(parseInt(value), "nCells") - of "i", "index": - args.index = parseInt(value) of "r1cs": files.r1cs = value.expectPath() of "wasm":