mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-01-26 17:23:14 +00:00
209 lines
5.6 KiB
Nim
209 lines
5.6 KiB
Nim
## Nim-Codex
|
|
## Copyright (c) 2025 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 std/sugar
|
|
import std/isolation
|
|
import std/atomics
|
|
|
|
import pkg/chronos
|
|
import pkg/chronos/threadsync
|
|
import pkg/taskpools
|
|
import pkg/questionable/results
|
|
|
|
import pkg/groth16
|
|
import pkg/nim/circom_witnessgen
|
|
import pkg/nim/circom_witnessgen/load
|
|
import pkg/nim/circom_witnessgen/witness
|
|
|
|
import ../../types
|
|
import ../../../stores
|
|
import ../../../contracts
|
|
|
|
import ./converters
|
|
|
|
export converters
|
|
|
|
const DefaultCurve* = "bn128"
|
|
|
|
type
|
|
NimGroth16Backend* = object
|
|
curve: string # curve name
|
|
slotDepth: int # max depth of the slot tree
|
|
datasetDepth: int # max depth of dataset tree
|
|
blkDepth: int # depth of the block merkle tree (pow2 for now)
|
|
cellElms: int # number of field elements per cell
|
|
numSamples: int # number of samples per slot
|
|
r1cs: R1CS # path to the r1cs file
|
|
zkey: ZKey # path to the zkey file
|
|
graph*: Graph # path to the graph file generated with circom-witnesscalc
|
|
tp: Taskpool # taskpool for async operations
|
|
|
|
NimGroth16BackendRef* = ref NimGroth16Backend
|
|
|
|
ProofTask* = object
|
|
proof: Isolated[Proof]
|
|
self: ptr NimGroth16Backend
|
|
inputs: Inputs
|
|
signal: ThreadSignalPtr
|
|
ok: Atomic[bool]
|
|
|
|
proc release*(self: NimGroth16BackendRef) =
|
|
## Release the ctx
|
|
##
|
|
|
|
discard
|
|
|
|
proc normalizeInput[SomeHash](
|
|
self: NimGroth16BackendRef, input: ProofInputs[SomeHash]
|
|
): Inputs =
|
|
## Map inputs to witnessgen inputs
|
|
##
|
|
|
|
var normSlotProof = input.slotProof
|
|
normSlotProof.setLen(self.datasetDepth)
|
|
|
|
{
|
|
"slotDepth": @[self.slotDepth.toF],
|
|
"datasetDepth": @[self.datasetDepth.toF],
|
|
"blkDepth": @[self.blkDepth.toF],
|
|
"cellElms": @[self.cellElms.toF],
|
|
"numSamples": @[self.numSamples.toF],
|
|
"entropy": @[input.entropy],
|
|
"dataSetRoot": @[input.datasetRoot],
|
|
"slotIndex": @[input.slotIndex.toF],
|
|
"slotRoot": @[input.slotRoot],
|
|
"nCellsPerSlot": @[input.nCellsPerSlot.toF],
|
|
"nSlotsPerDataSet": @[input.nSlotsPerDataSet.toF],
|
|
"slotProof": normSlotProof,
|
|
"cellData": input.samples.mapIt(it.cellData).concat,
|
|
"merklePaths": input.samples.mapIt(
|
|
block:
|
|
var mekrlePaths = it.merklePaths
|
|
mekrlePaths.setLen(self.slotDepth)
|
|
mekrlePaths
|
|
).concat,
|
|
}.toTable
|
|
|
|
proc generateProofTask(task: ptr ProofTask) =
|
|
defer:
|
|
if task[].signal != nil:
|
|
discard task[].signal.fireSync()
|
|
|
|
try:
|
|
trace "Generating witness"
|
|
let
|
|
witnessValues = generateWitness(task[].self[].graph, task[].inputs)
|
|
witness = Witness(
|
|
curve: task[].self[].curve,
|
|
r: task[].self[].r1cs.r,
|
|
nvars: task[].self[].r1cs.cfg.nWires,
|
|
values: witnessValues,
|
|
)
|
|
|
|
trace "Generating nim groth16 proof"
|
|
var proof = generateProof(task[].self[].zkey, witness, task[].self[].tp)
|
|
trace "Proof generated, copying to main thread"
|
|
var isolatedProof = isolate(proof)
|
|
task[].proof = move isolatedProof
|
|
task[].ok.store true
|
|
except CatchableError as e:
|
|
error "Failed to generate proof", err = e.msg
|
|
task[].ok.store false
|
|
|
|
proc prove*[SomeHash](
|
|
self: NimGroth16BackendRef, input: ProofInputs[SomeHash]
|
|
): Future[?!NimGroth16Proof] {.async: (raises: [CancelledError]).} =
|
|
## Prove a statement using backend.
|
|
##
|
|
|
|
var
|
|
signalPtr = ?ThreadSignalPtr.new().mapFailure
|
|
task = ProofTask(
|
|
self: cast[ptr NimGroth16Backend](self),
|
|
signal: signalPtr,
|
|
inputs: self.normalizeInput(input),
|
|
)
|
|
|
|
defer:
|
|
if signalPtr != nil:
|
|
?signalPtr.close().mapFailure
|
|
signalPtr = nil
|
|
|
|
self.tp.spawn generateProofTask(task.addr)
|
|
|
|
let taskFut = signalPtr.wait()
|
|
if err =? catch(await taskFut.join()).errorOption:
|
|
# XXX: we need this because there is no way to cancel a task
|
|
# and without waiting for it to finish, we'll be writting to free'd
|
|
# memory in the task
|
|
warn "Error while generating proof, awaiting task to finish", err = err.msg
|
|
?catch(await noCancel taskFut)
|
|
if err of CancelledError: # reraise cancelled error
|
|
trace "Task was cancelled"
|
|
raise (ref CancelledError) err
|
|
|
|
trace "Task failed with error", err = err.msg
|
|
return failure err
|
|
|
|
defer:
|
|
task.proof = default(Isolated[Proof])
|
|
|
|
if not task.ok.load:
|
|
trace "Task failed, no proof generated"
|
|
return failure("Failed to generate proof")
|
|
|
|
var proof = task.proof.extract
|
|
trace "Task finished successfully, proof generated"
|
|
success proof
|
|
|
|
proc verify*(
|
|
self: NimGroth16BackendRef, proof: NimGroth16Proof
|
|
): Future[?!bool] {.async: (raises: [CancelledError]).} =
|
|
let
|
|
vKey = self.zkey.extractVKey
|
|
verified = ?verifyProof(vKey, proof).catch
|
|
|
|
success verified
|
|
|
|
proc new*(
|
|
_: type NimGroth16BackendRef,
|
|
graphPath: string,
|
|
r1csPath: string,
|
|
zkeyPath: string,
|
|
curve = DefaultCurve,
|
|
slotDepth = DefaultMaxSlotDepth,
|
|
datasetDepth = DefaultMaxDatasetDepth,
|
|
blkDepth = DefaultBlockDepth,
|
|
cellElms = DefaultCellElms,
|
|
numSamples = DefaultSamplesNum,
|
|
tp: Taskpool,
|
|
): ?!NimGroth16BackendRef =
|
|
## Create a new ctx
|
|
##
|
|
|
|
let
|
|
graph = ?loadGraph(graphPath).catch
|
|
r1cs = ?parseR1CS(r1csPath).catch
|
|
zkey = ?parseZKey(zkeyPath).catch
|
|
|
|
success NimGroth16BackendRef(
|
|
graph: graph,
|
|
r1cs: r1cs,
|
|
zkey: zkey,
|
|
slotDepth: slotDepth,
|
|
datasetDepth: datasetDepth,
|
|
blkDepth: blkDepth,
|
|
cellElms: cellElms,
|
|
numSamples: numSamples,
|
|
curve: curve,
|
|
tp: tp,
|
|
)
|