Prover works with zkey (#711)

* rework backend to instantiate key at initialization

* add groth16 convertes for solidity

* prover taks num samples on construction

* add zkey file

* rework helpers

* rename types

* update tests

* reworked test helpers

* rename types

* rework test

* test all slots artifacts

* bump to latest version
This commit is contained in:
Dmitriy Ryajov 2024-02-19 12:58:39 -06:00 committed by GitHub
parent 6b86601597
commit 9e13d2251a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 233 additions and 186 deletions

View File

@ -20,8 +20,11 @@ import ../../types
import ../../../stores import ../../../stores
import ../../../merkletree import ../../../merkletree
import ../../../codextypes import ../../../codextypes
import ../../../contracts
export circomcompat import ./converters
export circomcompat, converters
type type
CircomCompat* = object CircomCompat* = object
@ -32,40 +35,24 @@ type
numSamples : int # number of samples per slot numSamples : int # number of samples per slot
r1csPath : string # path to the r1cs file r1csPath : string # path to the r1cs file
wasmPath : string # path to the wasm file wasmPath : string # path to the wasm file
zKeyPath : string # path to the zkey file zkeyPath : string # path to the zkey file
backendCfg : ptr CircomBn254Cfg backendCfg : ptr CircomBn254Cfg
vkp* : ptr CircomKey
CircomG1* = G1
CircomG2* = G2
CircomProof* = Proof
CircomKey* = VerifyingKey
CircomInputs* = Inputs
proc release*(self: CircomCompat) = proc release*(self: CircomCompat) =
## Release the backend ## Release the ctx
## ##
self.backendCfg.unsafeAddr.releaseCfg() if not isNil(self.backendCfg):
self.backendCfg.unsafeAddr.releaseCfg()
proc getVerifyingKey*( if not isNil(self.vkp):
self: CircomCompat): ?!ptr CircomKey = self.vkp.unsafeAddr.release_key()
## Get the verifying key
##
var
cfg: ptr CircomBn254Cfg = self.backendCfg
vkpPtr: ptr VerifyingKey = nil
if cfg.getVerifyingKey(vkpPtr.addr) != ERR_OK or vkpPtr == nil:
return failure("Failed to get verifying key")
success vkpPtr
proc prove*[H]( proc prove*[H](
self: CircomCompat, self: CircomCompat,
input: ProofInput[H]): ?!CircomProof = input: ProofInputs[H]): ?!CircomProof =
## Encode buffers using a backend ## Encode buffers using a ctx
## ##
# NOTE: All inputs are statically sized per circuit # NOTE: All inputs are statically sized per circuit
@ -86,43 +73,43 @@ proc prove*[H](
# TODO: All parameters should match circom's static parametter # TODO: All parameters should match circom's static parametter
var var
backend: ptr CircomCompatCtx ctx: ptr CircomCompatCtx
defer: defer:
if backend != nil: if ctx != nil:
backend.addr.releaseCircomCompat() ctx.addr.releaseCircomCompat()
if initCircomCompat( if initCircomCompat(
self.backendCfg, self.backendCfg,
addr backend) != ERR_OK or backend == nil: addr ctx) != ERR_OK or ctx == nil:
raiseAssert("failed to initialize CircomCompat backend") raiseAssert("failed to initialize CircomCompat ctx")
var var
entropy = input.entropy.toBytes entropy = input.entropy.toBytes
dataSetRoot = input.datasetRoot.toBytes dataSetRoot = input.datasetRoot.toBytes
slotRoot = input.slotRoot.toBytes slotRoot = input.slotRoot.toBytes
if backend.pushInputU256Array( if ctx.pushInputU256Array(
"entropy".cstring, entropy[0].addr, entropy.len.uint32) != ERR_OK: "entropy".cstring, entropy[0].addr, entropy.len.uint32) != ERR_OK:
return failure("Failed to push entropy") return failure("Failed to push entropy")
if backend.pushInputU256Array( if ctx.pushInputU256Array(
"dataSetRoot".cstring, dataSetRoot[0].addr, dataSetRoot.len.uint32) != ERR_OK: "dataSetRoot".cstring, dataSetRoot[0].addr, dataSetRoot.len.uint32) != ERR_OK:
return failure("Failed to push data set root") return failure("Failed to push data set root")
if backend.pushInputU256Array( if ctx.pushInputU256Array(
"slotRoot".cstring, slotRoot[0].addr, slotRoot.len.uint32) != ERR_OK: "slotRoot".cstring, slotRoot[0].addr, slotRoot.len.uint32) != ERR_OK:
return failure("Failed to push data set root") return failure("Failed to push data set root")
if backend.pushInputU32( if ctx.pushInputU32(
"nCellsPerSlot".cstring, input.nCellsPerSlot.uint32) != ERR_OK: "nCellsPerSlot".cstring, input.nCellsPerSlot.uint32) != ERR_OK:
return failure("Failed to push nCellsPerSlot") return failure("Failed to push nCellsPerSlot")
if backend.pushInputU32( if ctx.pushInputU32(
"nSlotsPerDataSet".cstring, input.nSlotsPerDataSet.uint32) != ERR_OK: "nSlotsPerDataSet".cstring, input.nSlotsPerDataSet.uint32) != ERR_OK:
return failure("Failed to push nSlotsPerDataSet") return failure("Failed to push nSlotsPerDataSet")
if backend.pushInputU32( if ctx.pushInputU32(
"slotIndex".cstring, input.slotIndex.uint32) != ERR_OK: "slotIndex".cstring, input.slotIndex.uint32) != ERR_OK:
return failure("Failed to push slotIndex") return failure("Failed to push slotIndex")
@ -132,7 +119,7 @@ proc prove*[H](
slotProof.setLen(self.datasetDepth) # zero pad inputs to correct size slotProof.setLen(self.datasetDepth) # zero pad inputs to correct size
# arrays are always flattened # arrays are always flattened
if backend.pushInputU256Array( if ctx.pushInputU256Array(
"slotProof".cstring, "slotProof".cstring,
slotProof[0].addr, slotProof[0].addr,
uint (slotProof[0].len * slotProof.len)) != ERR_OK: uint (slotProof[0].len * slotProof.len)) != ERR_OK:
@ -144,14 +131,14 @@ proc prove*[H](
data = s.cellData data = s.cellData
merklePaths.setLen(self.slotDepth) # zero pad inputs to correct size merklePaths.setLen(self.slotDepth) # zero pad inputs to correct size
if backend.pushInputU256Array( if ctx.pushInputU256Array(
"merklePaths".cstring, "merklePaths".cstring,
merklePaths[0].addr, merklePaths[0].addr,
uint (merklePaths[0].len * merklePaths.len)) != ERR_OK: uint (merklePaths[0].len * merklePaths.len)) != ERR_OK:
return failure("Failed to push merkle paths") return failure("Failed to push merkle paths")
data.setLen(self.cellElms * 32) # zero pad inputs to correct size data.setLen(self.cellElms * 32) # zero pad inputs to correct size
if backend.pushInputU256Array( if ctx.pushInputU256Array(
"cellData".cstring, "cellData".cstring,
data[0].addr, data[0].addr,
data.len.uint) != ERR_OK: data.len.uint) != ERR_OK:
@ -163,7 +150,7 @@ proc prove*[H](
let proof = let proof =
try: try:
if ( if (
let res = self.backendCfg.proveCircuit(backend, proofPtr.addr); let res = self.backendCfg.proveCircuit(ctx, proofPtr.addr);
res != ERR_OK) or res != ERR_OK) or
proofPtr == nil: proofPtr == nil:
return failure("Failed to prove - err code: " & $res) return failure("Failed to prove - err code: " & $res)
@ -175,55 +162,66 @@ proc prove*[H](
success proof success proof
proc verify*( proc verify*[H](
self: CircomCompat, self: CircomCompat,
proof: CircomProof, proof: CircomProof,
inputs: CircomInputs, inputs: ProofInputs[H]): ?!bool =
vkp: CircomKey): ?!bool = ## Verify a proof using a ctx
## Verify a proof using a backend
## ##
var var
proofPtr : ptr Proof = unsafeAddr proof proofPtr = unsafeAddr proof
inputsPtr: ptr Inputs = unsafeAddr inputs inputs = inputs.toCircomInputs()
vpkPtr: ptr CircomKey = unsafeAddr vkp
let res = verifyCircuit(proofPtr, inputsPtr, vpkPtr) try:
if res == ERR_OK: let res = verifyCircuit(proofPtr, inputs.addr, self.vkp)
success true if res == ERR_OK:
elif res == ERR_FAILED_TO_VERIFY_PROOF: success true
success false elif res == ERR_FAILED_TO_VERIFY_PROOF:
else: success false
failure("Failed to verify proof - err code: " & $res) else:
failure("Failed to verify proof - err code: " & $res)
finally:
inputs.releaseCircomInputs()
proc init*( proc init*(
_: type CircomCompat, _: type CircomCompat,
r1csPath : string, r1csPath : string,
wasmPath : string, wasmPath : string,
zKeyPath : string = "", zkeyPath : string = "",
slotDepth = DefaultMaxSlotDepth, slotDepth = DefaultMaxSlotDepth,
datasetDepth = DefaultMaxDatasetDepth, datasetDepth = DefaultMaxDatasetDepth,
blkDepth = DefaultBlockDepth, blkDepth = DefaultBlockDepth,
cellElms = DefaultCellElms, cellElms = DefaultCellElms,
numSamples = DefaultSamplesNum): CircomCompat = numSamples = DefaultSamplesNum): CircomCompat =
## Create a new backend ## Create a new ctx
## ##
var cfg: ptr CircomBn254Cfg var cfg: ptr CircomBn254Cfg
var zkey = if zkeyPath.len > 0: zkeyPath.cstring else: nil
if initCircomConfig( if initCircomConfig(
r1csPath.cstring, r1csPath.cstring,
wasmPath.cstring, wasmPath.cstring,
if zKeyPath.len > 0: zKeyPath.cstring else: nil, zkey, cfg.addr) != ERR_OK or cfg == nil:
addr cfg) != ERR_OK or cfg == nil: if cfg != nil: cfg.addr.releaseCfg()
raiseAssert("failed to initialize circom compat config") 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( CircomCompat(
r1csPath : r1csPath, r1csPath : r1csPath,
wasmPath : wasmPath, wasmPath : wasmPath,
zKeyPath : zKeyPath, zkeyPath : zkeyPath,
backendCfg : cfg,
slotDepth : slotDepth, slotDepth : slotDepth,
datasetDepth: datasetDepth, datasetDepth: datasetDepth,
blkDepth : blkDepth, blkDepth : blkDepth,
cellElms : cellElms, cellElms : cellElms,
numSamples : numSamples) numSamples : numSamples,
backendCfg : cfg,
vkp : vkpPtr)

View File

@ -0,0 +1,70 @@
## Nim-Codex
## Copyright (c) 2024 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
{.push raises: [].}
import pkg/circomcompat
import ../../../contracts
import ../../types
import ../../../merkletree
type
CircomG1* = G1
CircomG2* = G2
CircomProof* = Proof
CircomKey* = VerifyingKey
CircomInputs* = Inputs
proc toCircomInputs*(inputs: ProofInputs[Poseidon2Hash]): CircomInputs =
var
slotIndex = inputs.slotIndex.toF.toBytes.toArray32
datasetRoot = inputs.datasetRoot.toBytes.toArray32
entropy = inputs.entropy.toBytes.toArray32
elms = [
entropy,
datasetRoot,
slotIndex
]
let inputsPtr = allocShared0(32 * elms.len)
copyMem(inputsPtr, addr elms[0], elms.len * 32)
CircomInputs(
elms: cast[ptr array[32, byte]](inputsPtr),
len: elms.len.uint)
proc releaseCircomInputs*(inputs: var CircomInputs) =
if not inputs.elms.isNil:
deallocShared(inputs.elms)
inputs.elms = nil
func toG1*(g: CircomG1): G1Point =
G1Point(
x: UInt256.fromBytesLE(g.x),
y: UInt256.fromBytesLE(g.y))
func toG2*(g: CircomG2): G2Point =
G2Point(
x: [
UInt256.fromBytesLE(g.x[0]),
UInt256.fromBytesLE(g.x[1])
],
y: [
UInt256.fromBytesLE(g.y[0]),
UInt256.fromBytesLE(g.y[1])
])
func toGroth16Proof*(proof: CircomProof): Groth16Proof =
Groth16Proof(
a: proof.a.toG1,
b: proof.b.toG2,
c: proof.c.toG1)

View File

@ -30,25 +30,27 @@ import ../types
export backends export backends
type logScope:
AnyProof* = CircomProof topics = "codex prover"
AnyInputs* = CircomInputs
AnyKeys* = CircomKey
AnyHash* = Poseidon2Hash
AnyBackend* = CircomCompat
AnyBuilder* = Poseidon2Builder
AnySampler* = Poseidon2Sampler
type
AnyBackend* = CircomCompat
AnyProof* = CircomProof
AnySampler* = Poseidon2Sampler
AnyBuilder* = Poseidon2Builder
AnyProofInputs* = ProofInputs[Poseidon2Hash]
Prover* = ref object of RootObj Prover* = ref object of RootObj
backend: AnyBackend backend: AnyBackend
store: BlockStore store: BlockStore
nSamples: int
proc prove*( proc prove*(
self: Prover, self: Prover,
slotIdx: int, slotIdx: int,
manifest: Manifest, manifest: Manifest,
challenge: ProofChallenge, challenge: ProofChallenge): Future[?!(AnyProofInputs, AnyProof)] {.async.} =
nSamples = DefaultSamplesNum): Future[?!AnyProof] {.async.} =
## Prove a statement using backend. ## Prove a statement using backend.
## Returns a future that resolves to a proof. ## Returns a future that resolves to a proof.
@ -67,7 +69,7 @@ proc prove*(
error "Unable to create data sampler", err = err.msg error "Unable to create data sampler", err = err.msg
return failure(err) return failure(err)
without proofInput =? await sampler.getProofInput(challenge, nSamples), err: without proofInput =? await sampler.getProofInput(challenge, self.nSamples), err:
error "Unable to get proof input for slot", err = err.msg error "Unable to get proof input for slot", err = err.msg
return failure(err) return failure(err)
@ -76,23 +78,24 @@ proc prove*(
error "Unable to prove slot", err = err.msg error "Unable to prove slot", err = err.msg
return failure(err) return failure(err)
success proof success (proofInput, proof)
proc verify*( proc verify*(
self: Prover, self: Prover,
proof: AnyProof, proof: AnyProof,
inputs: AnyInputs, inputs: AnyProofInputs): Future[?!bool] {.async.} =
vpk: AnyKeys): Future[?!bool] {.async.} =
## Prove a statement using backend. ## Prove a statement using backend.
## Returns a future that resolves to a proof. ## Returns a future that resolves to a proof.
self.backend.verify(proof, inputs, vpk) self.backend.verify(proof, inputs)
proc new*( proc new*(
_: type Prover, _: type Prover,
store: BlockStore, store: BlockStore,
backend: AnyBackend): Prover = backend: AnyBackend,
nSamples: int): Prover =
Prover( Prover(
backend: backend, backend: backend,
store: store) store: store,
nSamples: nSamples)

View File

@ -91,7 +91,7 @@ proc getSample*[T, H](
proc getProofInput*[T, H]( proc getProofInput*[T, H](
self: DataSampler[T, H], self: DataSampler[T, H],
entropy: ProofChallenge, entropy: ProofChallenge,
nSamples: Natural): Future[?!ProofInput[H]] {.async.} = nSamples: Natural): Future[?!ProofInputs[H]] {.async.} =
## Generate proofs as input to the proving circuit. ## Generate proofs as input to the proving circuit.
## ##
@ -124,7 +124,7 @@ proc getProofInput*[T, H](
(await self.getSample(cellIdx, slotTreeCid, slotRoot)).valueOr: (await self.getSample(cellIdx, slotTreeCid, slotRoot)).valueOr:
return failure("Failed to get sample") return failure("Failed to get sample")
success ProofInput[H]( success ProofInputs[H](
entropy: entropy, entropy: entropy,
datasetRoot: datasetRoot, datasetRoot: datasetRoot,
slotProof: slotProof.path, slotProof: slotProof.path,

View File

@ -17,7 +17,7 @@ type
datasetRoot*: H datasetRoot*: H
entropy*: H entropy*: H
ProofInput*[H] = object ProofInputs*[H] = object
entropy*: H entropy*: H
datasetRoot*: H datasetRoot*: H
slotIndex*: Natural slotIndex*: Natural

Binary file not shown.

View File

@ -2,8 +2,10 @@
import std/sequtils import std/sequtils
import std/sugar import std/sugar
import std/strutils import std/strutils
import std/options
import pkg/poseidon2 import pkg/poseidon2
import pkg/poseidon2/io
import pkg/constantine/math/arithmetic import pkg/constantine/math/arithmetic
import pkg/constantine/math/io/io_bigints import pkg/constantine/math/io/io_bigints
import pkg/constantine/math/io/io_fields import pkg/constantine/math/io/io_fields
@ -15,7 +17,7 @@ import pkg/codex/utils/json
export types export types
func fromCircomData*[H](cellData: seq[byte]): seq[H] = func fromCircomData*(_: type Poseidon2Hash, cellData: seq[byte]): seq[Poseidon2Hash] =
var var
pos = 0 pos = 0
cellElms: seq[Bn254Fr] cellElms: seq[Bn254Fr]
@ -30,43 +32,40 @@ func fromCircomData*[H](cellData: seq[byte]): seq[H] =
cellElms cellElms
func toPublicInputs*[H](input: ProofInput[H]): PublicInputs[H] =
PublicInputs[H](
slotIndex: input.slotIndex,
datasetRoot: input.datasetRoot,
entropy: input.entropy
)
proc toCircomInputs*[H](inputs: PublicInputs[H]): CircomInputs =
var
slotIndex = inputs.slotIndex.toF.toBytes.toArray32
datasetRoot = inputs.datasetRoot.toBytes.toArray32
entropy = inputs.entropy.toBytes.toArray32
elms = [
entropy,
datasetRoot,
slotIndex
]
let inputsPtr = allocShared0(32 * elms.len)
copyMem(inputsPtr, addr elms[0], elms.len * 32)
CircomInputs(
elms: cast[ptr array[32, byte]](inputsPtr),
len: elms.len.uint
)
proc releaseNimInputs*(inputs: var CircomInputs) =
if not inputs.elms.isNil:
deallocShared(inputs.elms)
inputs.elms = nil
func toJsonDecimal*(big: BigInt[254]): string = func toJsonDecimal*(big: BigInt[254]): string =
let s = big.toDecimal.strip( leading = true, trailing = false, chars = {'0'} ) let s = big.toDecimal.strip( leading = true, trailing = false, chars = {'0'} )
if s.len == 0: "0" else: s if s.len == 0: "0" else: s
func toJson*[H](input: ProofInput[H]): JsonNode = func toJson*(g1: CircomG1): JsonNode =
%* {
"x": Bn254Fr.fromBytes(g1.x).get.toBig.toJsonDecimal,
"y": Bn254Fr.fromBytes(g1.y).get.toBig.toJsonDecimal
}
func toJson*(g2: CircomG2): JsonNode =
%* {
"x": [
Bn254Fr.fromBytes(g2.x[0]).get.toBig.toJsonDecimal,
Bn254Fr.fromBytes(g2.x[1]).get.toBig.toJsonDecimal],
"y": [
Bn254Fr.fromBytes(g2.y[0]).get.toBig.toJsonDecimal,
Bn254Fr.fromBytes(g2.y[1]).get.toBig.toJsonDecimal]
}
proc toJson*(vpk: VerifyingKey): JsonNode =
let
ic = toSeq(cast[ptr UncheckedArray[CircomG1]](vpk.ic).toOpenArray(0, vpk.icLen.int - 1))
echo ic.len
%* {
"alpha1": vpk.alpha1.toJson,
"beta2": vpk.beta2.toJson,
"gamma2": vpk.gamma2.toJson,
"delta2": vpk.delta2.toJson,
"ic": ic.mapIt( it.toJson )
}
func toJson*(input: ProofInputs[Poseidon2Hash]): JsonNode =
var var
input = input input = input
@ -79,14 +78,14 @@ func toJson*[H](input: ProofInput[H]): JsonNode =
"slotRoot": input.slotRoot.toDecimal, "slotRoot": input.slotRoot.toDecimal,
"slotProof": input.slotProof.mapIt( it.toBig.toJsonDecimal ), "slotProof": input.slotProof.mapIt( it.toBig.toJsonDecimal ),
"cellData": input.samples.mapIt( "cellData": input.samples.mapIt(
toSeq( it.cellData.elements(H) ).mapIt( it.toBig.toJsonDecimal ) toSeq( it.cellData.elements(Poseidon2Hash) ).mapIt( it.toBig.toJsonDecimal )
), ),
"merklePaths": input.samples.mapIt( "merklePaths": input.samples.mapIt(
it.merklePaths.mapIt( it.toBig.toJsonDecimal ) it.merklePaths.mapIt( it.toBig.toJsonDecimal )
) )
} }
func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] = func jsonToProofInput*(_: type Poseidon2Hash, inputJson: JsonNode): ProofInputs[Poseidon2Hash] =
let let
cellData = cellData =
inputJson["cellData"].mapIt( inputJson["cellData"].mapIt(
@ -107,7 +106,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
block: block:
var var
big: BigInt[254] big: BigInt[254]
hash: H hash: Poseidon2Hash
assert bool(big.fromDecimal( it.getStr )) assert bool(big.fromDecimal( it.getStr ))
hash.fromBig( big ) hash.fromBig( big )
hash hash
@ -118,7 +117,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
block: block:
var var
big: BigInt[254] big: BigInt[254]
hash: H hash: Poseidon2Hash
assert bool(big.fromDecimal( it.str )) assert bool(big.fromDecimal( it.str ))
hash.fromBig( big ) hash.fromBig( big )
hash hash
@ -127,7 +126,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
datasetRoot = block: datasetRoot = block:
var var
big: BigInt[254] big: BigInt[254]
hash: H hash: Poseidon2Hash
assert bool(big.fromDecimal( inputJson["dataSetRoot"].str )) assert bool(big.fromDecimal( inputJson["dataSetRoot"].str ))
hash.fromBig( big ) hash.fromBig( big )
hash hash
@ -135,7 +134,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
slotRoot = block: slotRoot = block:
var var
big: BigInt[254] big: BigInt[254]
hash: H hash: Poseidon2Hash
assert bool(big.fromDecimal( inputJson["slotRoot"].str )) assert bool(big.fromDecimal( inputJson["slotRoot"].str ))
hash.fromBig( big ) hash.fromBig( big )
hash hash
@ -143,7 +142,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
entropy = block: entropy = block:
var var
big: BigInt[254] big: BigInt[254]
hash: H hash: Poseidon2Hash
assert bool(big.fromDecimal( inputJson["entropy"].str )) assert bool(big.fromDecimal( inputJson["entropy"].str ))
hash.fromBig( big ) hash.fromBig( big )
hash hash
@ -152,7 +151,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
nSlotsPerDataSet = inputJson["nSlotsPerDataSet"].getInt nSlotsPerDataSet = inputJson["nSlotsPerDataSet"].getInt
slotIndex = inputJson["slotIndex"].getInt slotIndex = inputJson["slotIndex"].getInt
ProofInput[H]( ProofInputs[Poseidon2Hash](
entropy: entropy, entropy: entropy,
slotIndex: slotIndex, slotIndex: slotIndex,
datasetRoot: datasetRoot, datasetRoot: datasetRoot,
@ -161,7 +160,7 @@ func jsonToProofInput*[H](inputJson: JsonNode): ProofInput[H] =
nCellsPerSlot: nCellsPerSlot, nCellsPerSlot: nCellsPerSlot,
nSlotsPerDataSet: nSlotsPerDataSet, nSlotsPerDataSet: nSlotsPerDataSet,
samples: zip(cellData, merklePaths) samples: zip(cellData, merklePaths)
.mapIt(Sample[H]( .mapIt(Sample[Poseidon2Hash](
cellData: it[0], cellData: it[0],
merklePaths: it[1] merklePaths: it[1]
)) ))

View File

@ -3,8 +3,9 @@ import std/sequtils
import std/sugar import std/sugar
import std/options import std/options
import pkg/chronos
import ../../../asynctest import ../../../asynctest
import pkg/chronos
import pkg/poseidon2 import pkg/poseidon2
import pkg/datastore import pkg/datastore
@ -16,10 +17,6 @@ import pkg/codex/codextypes
import pkg/codex/manifest import pkg/codex/manifest
import pkg/codex/stores import pkg/codex/stores
import pkg/constantine/math/arithmetic
import pkg/constantine/math/io/io_fields
import pkg/constantine/math/io/io_bigints
import ./helpers import ./helpers
import ../helpers import ../helpers
@ -27,56 +24,50 @@ suite "Test Circom Compat Backend - control inputs":
let let
r1cs = "tests/circuits/fixtures/proof_main.r1cs" r1cs = "tests/circuits/fixtures/proof_main.r1cs"
wasm = "tests/circuits/fixtures/proof_main.wasm" wasm = "tests/circuits/fixtures/proof_main.wasm"
zkey = "tests/circuits/fixtures/proof_main.zkey"
var var
circom: CircomCompat circom: CircomCompat
verifyingKeyPtr: ptr CircomKey proofInputs: ProofInputs[Poseidon2Hash]
proofInput: ProofInput[Poseidon2Hash]
publicInputs: CircomInputs
setup: setup:
let let
inputData = readFile("tests/circuits/fixtures/input.json") inputData = readFile("tests/circuits/fixtures/input.json")
inputJson = parseJson(inputData) inputJson = parseJson(inputData)
proofInput = jsonToProofInput[Poseidon2Hash](inputJson) proofInputs = Poseidon2Hash.jsonToProofInput(inputJson)
publicInputs = toPublicInputs[Poseidon2Hash](proofInput).toCircomInputs circom = CircomCompat.init(r1cs, wasm, zkey)
# circom = CircomCompat.init(r1cs, wasm, zkey)
circom = CircomCompat.init(r1cs, wasm)
verifyingKeyPtr = circom.getVerifyingKey().tryGet
teardown: teardown:
publicInputs.releaseNimInputs() # this is allocated by nim circom.release() # this comes from the rust FFI
verifyingKeyPtr.addr.releaseKey() # this comes from the rust FFI
circom.release() # this comes from the rust FFI
test "Should verify with correct inputs": test "Should verify with correct inputs":
let let
proof = circom.prove(proofInput).tryGet proof = circom.prove(proofInputs).tryGet
check circom.verify(proof, publicInputs, verifyingKeyPtr[]).tryGet check circom.verify(proof, proofInputs).tryGet
test "Should not verify with incorrect inputs": test "Should not verify with incorrect inputs":
proofInput.slotIndex = 1 # change slot index proofInputs.slotIndex = 1 # change slot index
let let
proof = circom.prove(proofInput).tryGet proof = circom.prove(proofInputs).tryGet
check circom.verify(proof, publicInputs, verifyingKeyPtr[]).tryGet == false check circom.verify(proof, proofInputs).tryGet == false
suite "Test Circom Compat Backend": suite "Test Circom Compat Backend":
let let
slotId = 3
samples = 5
ecK = 2 ecK = 2
ecM = 2 ecM = 2
slotId = 3
samples = 5
numDatasetBlocks = 8 numDatasetBlocks = 8
blockSize = DefaultBlockSize blockSize = DefaultBlockSize
cellSize = DefaultCellSize cellSize = DefaultCellSize
r1cs = "tests/circuits/fixtures/proof_main.r1cs" r1cs = "tests/circuits/fixtures/proof_main.r1cs"
wasm = "tests/circuits/fixtures/proof_main.wasm" wasm = "tests/circuits/fixtures/proof_main.wasm"
zkey = "tests/circuits/fixtures/proof_main.zkey"
var var
store: BlockStore store: BlockStore
@ -84,9 +75,7 @@ suite "Test Circom Compat Backend":
protected: Manifest protected: Manifest
verifiable: Manifest verifiable: Manifest
circom: CircomCompat circom: CircomCompat
verifyingKeyPtr: ptr CircomKey proofInputs: ProofInputs[Poseidon2Hash]
proofInput: ProofInput[Poseidon2Hash]
publicInputs: CircomInputs
challenge: array[32, byte] challenge: array[32, byte]
builder: Poseidon2Builder builder: Poseidon2Builder
sampler: Poseidon2Sampler sampler: Poseidon2Sampler
@ -109,29 +98,24 @@ suite "Test Circom Compat Backend":
builder = Poseidon2Builder.new(store, verifiable).tryGet builder = Poseidon2Builder.new(store, verifiable).tryGet
sampler = Poseidon2Sampler.new(slotId, store, builder).tryGet sampler = Poseidon2Sampler.new(slotId, store, builder).tryGet
# circom = CircomCompat.init(r1cs, wasm, zkey) circom = CircomCompat.init(r1cs, wasm, zkey)
circom = CircomCompat.init(r1cs, wasm)
verifyingKeyPtr = circom.getVerifyingKey().tryGet
challenge = 1234567.toF.toBytes.toArray32 challenge = 1234567.toF.toBytes.toArray32
proofInput = (await sampler.getProofInput(challenge, samples)).tryGet proofInputs = (await sampler.getProofInput(challenge, samples)).tryGet
publicInputs = proofInput.toPublicInputs.toCircomInputs
teardown: teardown:
publicInputs.releaseNimInputs() # this is allocated by nim circom.release() # this comes from the rust FFI
verifyingKeyPtr.addr.releaseKey() # this comes from the rust FFI
circom.release() # this comes from the rust FFI
test "Should verify with correct input": test "Should verify with correct input":
var var
proof = circom.prove(proofInput).tryGet proof = circom.prove(proofInputs).tryGet
check circom.verify(proof, publicInputs, verifyingKeyPtr[]).tryGet check circom.verify(proof, proofInputs).tryGet
test "Should not verify with incorrect input": test "Should not verify with incorrect input":
proofInput.slotIndex = 1 # change slot index proofInputs.slotIndex = 1 # change slot index
let let
proof = circom.prove(proofInput).tryGet proof = circom.prove(proofInputs).tryGet
check circom.verify(proof, publicInputs, verifyingKeyPtr[]).tryGet == false check circom.verify(proof, proofInputs).tryGet == false

View File

@ -31,12 +31,12 @@ suite "Test Sampler - control samples":
var var
inputData: string inputData: string
inputJson: JsonNode inputJson: JsonNode
proofInput: ProofInput[Poseidon2Hash] proofInput: ProofInputs[Poseidon2Hash]
setup: setup:
inputData = readFile("tests/circuits/fixtures/input.json") inputData = readFile("tests/circuits/fixtures/input.json")
inputJson = parseJson(inputData) inputJson = parseJson(inputData)
proofInput = jsonToProofInput[Poseidon2Hash](inputJson) proofInput = Poseidon2Hash.jsonToProofInput(inputJson)
test "Should verify control samples": test "Should verify control samples":
let let
@ -58,7 +58,7 @@ suite "Test Sampler - control samples":
proofInput.nCellsPerSlot, proofInput.nCellsPerSlot,
sample.merklePaths[5..<9]).tryGet sample.merklePaths[5..<9]).tryGet
cellData = fromCircomData[Poseidon2Hash](sample.cellData) cellData = Poseidon2Hash.fromCircomData(sample.cellData)
cellLeaf = Poseidon2Hash.spongeDigest(cellData, rate = 2).tryGet cellLeaf = Poseidon2Hash.spongeDigest(cellData, rate = 2).tryGet
slotLeaf = cellProof.reconstructRoot(cellLeaf).tryGet slotLeaf = cellProof.reconstructRoot(cellLeaf).tryGet
@ -154,7 +154,7 @@ suite "Test Sampler":
nSlotCells, nSlotCells,
sample.merklePaths[5..<sample.merklePaths.len]).tryGet sample.merklePaths[5..<sample.merklePaths.len]).tryGet
cellData = fromCircomData[Poseidon2Hash](sample.cellData) cellData = Poseidon2Hash.fromCircomData(sample.cellData)
cellLeaf = Poseidon2Hash.spongeDigest(cellData, rate = 2).tryGet cellLeaf = Poseidon2Hash.spongeDigest(cellData, rate = 2).tryGet
slotLeaf = cellProof.reconstructRoot(cellLeaf).tryGet slotLeaf = cellProof.reconstructRoot(cellLeaf).tryGet

View File

@ -34,12 +34,12 @@ asyncchecksuite "Test proof sampler utils":
var var
inputData: string inputData: string
inputJson: JsonNode inputJson: JsonNode
proofInput: ProofInput[Poseidon2Hash] proofInput: ProofInputs[Poseidon2Hash]
setup: setup:
inputData = readFile("tests/circuits/fixtures/input.json") inputData = readFile("tests/circuits/fixtures/input.json")
inputJson = parseJson(inputData) inputJson = parseJson(inputData)
proofInput = jsonToProofInput[Poseidon2Hash](inputJson) proofInput = Poseidon2Hash.jsonToProofInput(inputJson)
test "Extract low bits": test "Extract low bits":
proc extract(value: uint64, nBits: int): uint64 = proc extract(value: uint64, nBits: int): uint64 =

View File

@ -18,10 +18,6 @@ import pkg/codex/stores
import pkg/poseidon2/io import pkg/poseidon2/io
import pkg/codex/utils/poseidon2digest import pkg/codex/utils/poseidon2digest
import pkg/constantine/math/arithmetic
import pkg/constantine/math/io/io_bigints
import pkg/constantine/math/io/io_fields
import ./helpers import ./helpers
import ../helpers import ../helpers
import ./backends/helpers import ./backends/helpers
@ -30,11 +26,11 @@ suite "Test Prover":
let let
slotId = 1 slotId = 1
samples = 5 samples = 5
blockSize = DefaultBlockSize
cellSize = DefaultCellSize
ecK = 3 ecK = 3
ecM = 2 ecM = 2
numDatasetBlocks = 8 numDatasetBlocks = 8
blockSize = DefaultBlockSize
cellSize = DefaultCellSize
var var
datasetBlocks: seq[bt.Block] datasetBlocks: seq[bt.Block]
@ -65,14 +61,9 @@ suite "Test Prover":
wasm = "tests/circuits/fixtures/proof_main.wasm" wasm = "tests/circuits/fixtures/proof_main.wasm"
circomBackend = CircomCompat.init(r1cs, wasm) circomBackend = CircomCompat.init(r1cs, wasm)
prover = Prover.new(store, circomBackend) prover = Prover.new(store, circomBackend, samples)
challenge = 1234567.toF.toBytes.toArray32 challenge = 1234567.toF.toBytes.toArray32
proof = (await prover.prove(1, verifiable, challenge, 5)).tryGet (inputs, proof) = (await prover.prove(1, verifiable, challenge)).tryGet
key = circomBackend.getVerifyingKey().tryGet
builder = Poseidon2Builder.new(store, verifiable).tryGet
sampler = Poseidon2Sampler.new(1, store, builder).tryGet
proofInput = (await sampler.getProofInput(challenge, 5)).tryGet
inputs = proofInput.toPublicInputs.toCircomInputs
check: check:
(await prover.verify(proof, inputs, key[])).tryGet == true (await prover.verify(proof, inputs)).tryGet == true

View File

@ -1,5 +1,7 @@
import ./slots/testslotbuilder import ./slots/testslotbuilder
import ./slots/testsampler import ./slots/testsampler
import ./slots/testconverters import ./slots/testconverters
import ./slots/testbackends
import ./slots/testprover
{.warning[UnusedImport]: off.} {.warning[UnusedImport]: off.}

@ -1 +1 @@
Subproject commit fa513b123e081c76ec0bb3237ad886d4830b8071 Subproject commit e710e4c333f367353aaa1ee82a55a47326b63a65