mirror of
https://github.com/logos-storage/nim-groth16.git
synced 2026-01-03 14:13:08 +00:00
177 lines
5.4 KiB
Nim
177 lines
5.4 KiB
Nim
|
|
#
|
|
# parsing the `.r1cs` file computed by `circom` witness code genereators
|
|
#
|
|
# file format
|
|
# ===========
|
|
#
|
|
# standard iden3 binary container format.
|
|
# field elements are in standard representation
|
|
#
|
|
# sections:
|
|
#
|
|
# 1: Header
|
|
# ---------
|
|
# n8r : word32 = how many bytes are a field element in Fr
|
|
# r : n8r bytes = the size of the prime field Fr (the scalar field)
|
|
# nWires : word32 = number of wires (or witness variables)
|
|
# nPubOut : word32 = number of public outputs
|
|
# nPubIn : word32 = number of public inputs
|
|
# nPrivIn : word32 = number of private inputs
|
|
# nLabels : word64 = number of labels (variable names in the circom source code)
|
|
#
|
|
# 2: Constraints
|
|
# --------------
|
|
# nConstr : word32 = number of constraints
|
|
# then an array of constraints:
|
|
# A : LinComb
|
|
# B : LinComb
|
|
# C : LinComb
|
|
# meaning `A*B=C`, where LinComb looks like this:
|
|
# nTerms : word32 = number of terms
|
|
# <an array of terms>
|
|
# where a term looks like this:
|
|
# idx : word32 = which witness variable
|
|
# coeff : Fr = the coefficient
|
|
#
|
|
# 3: Wire-to-label mapping
|
|
# ------------------------
|
|
# <an array of `nWires` many 64 bit words>
|
|
#
|
|
# 4: Custom gates list
|
|
# --------------------
|
|
# ...
|
|
# ...
|
|
#
|
|
# 4: Custom gates application
|
|
# ---------------------------
|
|
# ...
|
|
# ...
|
|
#
|
|
|
|
import std/streams
|
|
|
|
import constantine/math/arithmetic except Fp, Fr
|
|
import constantine/math/io/io_bigints
|
|
|
|
import ./bn128
|
|
import ./container
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
type
|
|
|
|
WitnessConfig* = object
|
|
nWires* : int # total number of wires (or witness variables), including the constant 1 "variable"
|
|
nPubOut* : int # number of public outputs
|
|
nPubIn* : int # number of public inputs
|
|
nPrivIn* : int # number of private inputs
|
|
nLabels* : int # number of labels
|
|
|
|
Term* = tuple[ wireIdx: int, value: Fr ]
|
|
LinComb* = seq[Term]
|
|
Constraint* = tuple[ A: LinComb, B: LinComb, C: LinComb ]
|
|
|
|
R1CS* = object
|
|
r* : BigInt[256]
|
|
cfg* : WitnessConfig
|
|
nConstr* : int
|
|
constraints* : seq[Constraint]
|
|
wireToLabel* : seq[int]
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
proc parseSection1_header( stream: Stream, user: var R1CS, sectionLen: int ) =
|
|
# echo "\nparsing r1cs header"
|
|
|
|
let (n8r, r) = parsePrimeField( stream ) # size of the scalar field
|
|
user.r = r;
|
|
|
|
# echo("r = ",toDecimalBig(r))
|
|
|
|
assert( sectionLen == 4 + n8r + 16 + 8 + 4, "unexpected section length")
|
|
|
|
assert( bool(r == primeR) , "expecting the alt-bn128 curve" )
|
|
|
|
var cfg : WitnessConfig
|
|
|
|
cfg.nWires = int( stream.readUint32() )
|
|
cfg.nPubOut = int( stream.readUint32() )
|
|
cfg.nPubIn = int( stream.readUint32() )
|
|
cfg.nPrivIn = int( stream.readUint32() )
|
|
cfg.nLabels = int( stream.readUint64() )
|
|
user.cfg = cfg
|
|
|
|
let nConstr = int( stream.readUint32() )
|
|
user.nConstr = nConstr
|
|
|
|
# echo("witness config = ",cfg)
|
|
# echo("nConstr = ",nConstr)
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
proc loadTerm( stream: Stream ): Term =
|
|
let idx = int( stream.readUint32() )
|
|
let coeff = loadValueFrStd( stream )
|
|
return (wireIdx:idx, value:coeff)
|
|
|
|
proc loadLinComb( stream: Stream ): LinComb =
|
|
let nterms = int( stream.readUint32() )
|
|
var terms : seq[Term]
|
|
for i in 1..nterms:
|
|
terms.add( loadTerm(stream) )
|
|
return terms
|
|
|
|
proc loadConstraint( stream: Stream ): Constraint =
|
|
let a = loadLinComb( stream )
|
|
let b = loadLinComb( stream )
|
|
let c = loadLinComb( stream )
|
|
return (A:a, B:b, C:c)
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
proc parseSection2_constraints( stream: Stream, user: var R1CS, sectionLen: int ) =
|
|
var constr: seq[Constraint]
|
|
var ncoeffsA, ncoeffsB, ncoeffsC: int
|
|
for i in 1..(user.nConstr):
|
|
let abc = loadConstraint(stream)
|
|
constr.add( abc )
|
|
ncoeffsA += abc.A.len
|
|
ncoeffsB += abc.B.len
|
|
ncoeffsC += abc.C.len
|
|
user.constraints = constr
|
|
# echo( "number of nonzero coefficients in matrix A = ", ncoeffsA )
|
|
# echo( "number of nonzero coefficients in matrix B = ", ncoeffsB )
|
|
# echo( "number of nonzero coefficients in matrix C = ", ncoeffsC )
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
proc parseSection3_wireToLabel( stream: Stream, user: var R1CS, sectionLen: int ) =
|
|
assert( sectionLen == 8 * user.cfg.nWires, "unexpected section length")
|
|
var labels: seq[int]
|
|
for i in 1..(user.cfg.nWires):
|
|
let label = int( stream.readUint64() )
|
|
labels.add( label )
|
|
user.wireToLabel = labels
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
proc r1csCallback( stream: Stream
|
|
, sectId: int
|
|
, sectLen: int
|
|
, user: var R1CS
|
|
) =
|
|
case sectId
|
|
of 1: parseSection1_header( stream, user, sectLen )
|
|
of 2: parseSection2_constraints( stream, user, sectLen )
|
|
of 3: parseSection3_wireToLabel( stream, user, sectLen )
|
|
else: discard
|
|
|
|
proc parseR1CS* (fname: string): R1CS =
|
|
var r1cs : R1CS
|
|
parseContainer( "r1cs", 1, fname, r1cs, r1csCallback, proc (id: int): bool = id == 1 )
|
|
parseContainer( "r1cs", 1, fname, r1cs, r1csCallback, proc (id: int): bool = id != 1 )
|
|
return r1cs
|
|
|
|
#-------------------------------------------------------------------------------
|