Skip to content

Commit dc8fced

Browse files
authored
Merge pull request #5 from l-adic/circom-program
Circom program
2 parents 1b6ed5c + 6acb31c commit dc8fced

23 files changed

+137
-689
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
dist-newstyle
33
www/circuit_final.zkey
44
www/circuit.wasm
5+
www/circuit.bin
56
output
67
.spago
78
.purs-repl

Diff for: .parcelrc

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"*.wasm": [
88
"@parcel/transformer-raw"
99
],
10+
"*.bin": [
11+
"@parcel/transformer-raw"
12+
],
1013
"*.zkey": [
1114
"@parcel/transformer-raw"
1215
]

Diff for: README.md

+17-6
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,30 @@ You should have a local ethereum node with an unlocked default account and the w
2626
## Contents
2727

2828
### The factors zk program
29-
A ZK program written in a Haskell DSL that expresses a factorization of a public input `n` into a product of secret inputs `a` and `b`. You can produce a circom compatible `r1cs` file for this program by running
29+
A ZK program written in a Haskell DSL that expresses a factorization of a public input `n` into a product of secret inputs `a` and `b`. The factorization must be non-trivial, i.e. `a /= 1 && b /= 1`.
30+
31+
You can produce a circom compatible `r1cs` file for this program by running
32+
33+
```
34+
> cabal run factors-cli -- compile --output-dir trusted-setup
35+
```
36+
37+
You should see the artifacts
3038

3139
```
32-
> cabal run factors
40+
trusted-setup
41+
├── circuit.bin
42+
├── circuit.r1cs
43+
└ ...
3344
```
3445

35-
You should see an artifact `trusted-setup/circuit.r1cs`.
46+
The `circuit.r1cs` file is the R1CS that is expected by snarkjs for proving/verification key generation. The `circuit.bin` is the binary serialization of the constraints represented by the compiled circuit. This file is required in order to evaluate the circuit, i.e. generate a witness.
3647

3748
### A factors program constraint solver
3849
A constraint solver applied to the `factors` program. You can produce a circom compatible WASM binary for this solver by running
3950

4051
```
41-
> cd factors-solver
52+
> cd wasm-solver
4253
> ./build-wasm
4354
```
4455

@@ -68,10 +79,10 @@ You can comple the contracts, build the purescript ffi, and deploy this smart co
6879
```
6980

7081
### A frontend application
71-
Assuming you have done the previous steps, copy the proving key to the `www` folder
82+
Assuming you have done the previous steps, copy the proving key and the compiled circuit to the `www` folder
7283

7384
```
74-
> cp trusted-setup/circuit_final.zkey www
85+
> cp trusted-setup/circuit_final.zkey trusted-setup/circuit.bin www
7586
```
7687

7788
You should see the `circuit.wasm` solver binary is already there. Assuming you have deployed the verifying contract, you can start the frontend:

Diff for: app/Prover/Prove.js

+24-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
1-
import { WASI } from "@bjorn3/browser_wasi_shim/dist";
1+
import { WASI, PreopenDirectory, File, OpenFile, ConsoleStdout } from "@bjorn3/browser_wasi_shim/dist";
22
import { groth16, wtns } from "snarkjs";
33

4+
async function load_external_file(path) {
5+
const response = await fetch(path);
6+
const buffer = await response.arrayBuffer();
7+
return new File(buffer);
8+
}
49

510
async function _fullProve(input) {
611
const wasmFile = "circuit.wasm";
712
const zkeyFile = "circuit_final.zkey";
8-
const wasi = new WASI([], [], []);
9-
let options = { additionalImports: { wasi_snapshot_preview1: wasi.wasiImport }};
10-
const w= {
13+
let fds = [
14+
new OpenFile(new File([])), // stdin
15+
ConsoleStdout.lineBuffered(msg => console.log(`[WASI stdout] ${msg}`)),
16+
ConsoleStdout.lineBuffered(msg => console.warn(`[WASI stderr] ${msg}`)),
17+
new PreopenDirectory("/", [
18+
["circuit.bin", await load_external_file("circuit.bin")]
19+
])
20+
];
21+
const wasi = new WASI([], [], fds, { debug: true });
22+
let options = {
23+
initializeWasiReactorModuleInstance: (instance) => {
24+
wasi.initialize(instance);
25+
},
26+
additionalWASMImports: {
27+
wasi_snapshot_preview1: wasi.wasiImport
28+
}
29+
};
30+
const w = {
1131
type: "mem"
1232
};
1333
await wtns.calculate(input, wasmFile, w, options);

Diff for: cabal.project

+5-7
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@ source-repository-package
1919
source-repository-package
2020
type: git
2121
location: https://github.com/l-adic/galois-fields.git
22-
tag: b0867ffdebda5043c80315a51b15e82ed25acba6
23-
--sha256: pYe2FTNHPTzZeTnOMe6S9eh3EOY6Hi6PjdfsNjPSOZQ=
22+
tag: 525521de7b985364f7e0c32222fc3b21fea8e530
23+
--sha256: hPbO7PElCJ3X6+WibL0EmdPlZqLq6stjn2r5auhsv08=
2424

2525
source-repository-package
2626
type: git
2727
location: https://github.com/l-adic/arithmetic-circuits.git
28-
tag: d243f4cd91fb0adae11c18c726ef884cf7ea7d0d
29-
--sha256: cmgHbYgMfPCjtZiLqqKMrvL+D8kIPGN9sE07iVqBS5Q=
30-
31-
index-state: 2023-10-15T12:29:38Z
32-
28+
tag: 77415e01245ff6cc3cc60e9062dcf3e986b9811f
29+
--sha256: jI3tAEGwOBqiCr6JqQfAws3X2RIp1eYbI9fXWutTMuE=
3330

31+
index-state: 2024-05-21T06:16:08Z

Diff for: factors-solver/.envrc

-1
This file was deleted.

Diff for: factors/app/Main.hs

-13
This file was deleted.

Diff for: factors/cli/Main.hs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{-# LANGUAGE TemplateHaskell #-}
2+
3+
module Main where
4+
5+
import Circom.CLI (defaultMain)
6+
import Protolude
7+
import ZK.Factors (Fr, factors)
8+
9+
main :: IO ()
10+
main = defaultMain "circuit" $ factors @Fr

Diff for: factors/factors.cabal

+5-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ common extensions
1313
DataKinds
1414
TypeApplications
1515
NoImplicitPrelude
16+
NamedFieldPuns
1617
RecordWildCards
1718
OverloadedStrings
1819

@@ -34,16 +35,13 @@ library
3435
hs-source-dirs: src
3536
default-language: GHC2021
3637

37-
executable factors
38+
executable factors-cli
3839
import: warnings, extensions, deps
3940
main-is: Main.hs
4041
build-depends:
41-
binary
42-
, factors
43-
, aeson
44-
, wl-pprint-text >=1.2.0
42+
factors
4543

46-
hs-source-dirs: app
44+
hs-source-dirs: cli
4745
default-language: GHC2021
4846

4947
test-suite factors-test
@@ -53,6 +51,7 @@ test-suite factors-test
5351
hs-source-dirs: test
5452
main-is: Main.hs
5553
build-depends:
54+
binary
5655
, factors
5756
, hspec
5857
, QuickCheck

Diff for: factors/src/ZK/Factors.hs

+10-21
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,26 @@
22
{-# OPTIONS_GHC -fno-warn-orphans #-}
33

44
module ZK.Factors
5-
( Factors (..),
6-
factors,
5+
( factors,
76
Fr,
87
)
98
where
109

1110
import Circuit
1211
import Circuit.Language
13-
import R1CS.Circom (circomReindexMap)
1412
import Data.Field.Galois (GaloisField, Prime)
1513
import Protolude
1614

1715
type Fr = Prime 21888242871839275222246405745257275088548364400416034343698204186575808495617
1816

19-
factorsE :: (GaloisField f, Hashable f) => ExprM f (Var Wire f Bool)
20-
factorsE = do
21-
n <- var_ <$> fieldInput Public "n"
17+
factors :: (GaloisField f, Hashable f) => ExprM f (Var Wire f 'TBool)
18+
factors = do
2219
a <- var_ <$> fieldInput Private "a"
2320
b <- var_ <$> fieldInput Private "b"
24-
boolOutput "out" $ eq_ n (a * b)
25-
26-
data Factors f = Factors
27-
{ factorsCircuit :: ArithCircuit f,
28-
factorsVars :: CircuitVars Text
29-
}
30-
31-
factors :: forall f. (GaloisField f, Hashable f) => Factors f
32-
factors =
33-
let BuilderState {..} = snd $ runCircuitBuilder (factorsE @f)
34-
f = circomReindexMap bsVars
35-
in Factors
36-
{ factorsCircuit = reindex f bsCircuit,
37-
factorsVars = reindex f bsVars
38-
}
21+
n <- var_ <$> fieldInput Public "n"
22+
let cs =
23+
[ neq_ n a,
24+
neq_ n b,
25+
eq_ n (a * b)
26+
]
27+
boolOutput "out" $ unAnd_ $ foldMap And_ cs

Diff for: factors/test/Main.hs

+34-12
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,49 @@
11
module Main (main) where
22

3-
import Circuit (solve, lookupVar, assignInputs)
3+
import Circom.R1CS (witnessFromCircomWitness)
4+
import Circom.Solver (CircomProgram (..), mkCircomProgram, nativeGenWitness)
5+
import Circuit
6+
import Circuit.Language
47
import qualified Data.Map as Map
58
import Protolude
9+
import R1CS (Witness (..))
610
import Test.Hspec
711
import Test.QuickCheck
8-
import ZK.Factors (Factors (..), Fr, factors)
12+
import ZK.Factors (Fr, factors)
13+
import Data.Binary (encode, decode)
914

1015
main :: IO ()
1116
main = hspec $ do
12-
let circuit = factorsCircuit $ factors @Fr
13-
vars = factorsVars $ factors @Fr
17+
let BuilderState {bsVars, bsCircuit} = snd $ runCircuitBuilder (factors @Fr)
18+
program = mkCircomProgram bsVars bsCircuit
19+
vars = cpVars program
1420
describe "Factors" $ do
15-
it "should accept valid factors" $ do
21+
it "can serialize/deserialize the program" $ do
22+
let a = decode (encode program)
23+
cpCircuit a `shouldBe` cpCircuit program
24+
25+
it "should accept valid factorizations" $
1626
property $
1727
\x y ->
18-
let inputs = assignInputs vars $ Map.fromList [("n", x * y), ("a", x), ("b", y)]
19-
w = solve vars circuit inputs
20-
in lookupVar vars "out" w === Just 1
21-
it "shouldn't accept invalid factors" $ do
28+
(x /= 1 && y /= 1) ==>
29+
let inputs = Map.fromList [("n", x * y), ("a", x), ("b", y)]
30+
Witness w =
31+
witnessFromCircomWitness $
32+
nativeGenWitness program inputs
33+
in lookupVar vars "out" w === Just 1
34+
it "shouldn't accept trivial factorizations" $
35+
property $ \x ->
36+
let inputs = Map.fromList [("n", x), ("a", 1), ("b", x)]
37+
Witness w =
38+
witnessFromCircomWitness $
39+
nativeGenWitness program inputs
40+
in lookupVar vars "out" w == Just 0
41+
it "shouldn't accept invalid factorizations" $
2242
property $
2343
\x y z ->
2444
(x * y /= z) ==>
25-
let inputs = assignInputs vars $ Map.fromList [("n", z), ("a", x), ("b", y)]
26-
w = solve vars circuit inputs
27-
in lookupVar vars "out" w == Just 0
45+
let inputs = Map.fromList [("n", z), ("a", x), ("b", y)]
46+
Witness w =
47+
witnessFromCircomWitness $
48+
nativeGenWitness program inputs
49+
in lookupVar vars "out" w == Just 0

Diff for: flake.nix

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
);
4242
defaultGHC = perGHC.${defaultGHCVersion};
4343

44-
ormoluLive = import factors-solver/default.nix {
44+
wasmSolver = import wasm-solver/default.nix {
4545
inherit pkgs inputs defaultGHC;
4646
};
4747
in
@@ -63,8 +63,8 @@
6363
withHoogle = false;
6464
exactDeps = false;
6565
};
66-
ormoluLive = ormoluLive.shell;
67-
ghcWasm = ormoluLive.ghcWasmShell;
66+
wasmSolver = wasmSolver.shell;
67+
ghcWasm = wasmSolver.ghcWasmShell;
6868
};
6969
legacyPackages = defaultGHC // perGHC;
7070
});

0 commit comments

Comments
 (0)