1
+ module Circom.CLI (defaultMain ) where
2
+
3
+ import Circom.R1CS (r1csToCircomR1CS )
4
+ import Circom.Solver (CircomProgram (.. ), mkCircomProgram , nativeGenWitness )
5
+ import Circuit.Arithmetic (CircuitVars (cvInputsLabels ), InputBidings (labelToVar ))
6
+ import Circuit.Language.Compile (BuilderState (.. ), ExprM , runCircuitBuilder )
7
+ import Data.Aeson (decodeFileStrict )
8
+ import Data.Aeson qualified as A
9
+ import Data.Aeson.Encode.Pretty (encodePretty )
10
+ import Data.Binary (decode , decodeFile , encodeFile )
11
+ import Data.ByteString.Lazy qualified as LBS
12
+ import Data.Field.Galois (PrimeField )
13
+ import Data.Text qualified as Text
14
+ import Options.Applicative (CommandFields , Mod , Parser , ParserInfo , command , execParser , fullDesc , header , help , helper , hsubparser , info , long , progDesc , showDefault , strOption , switch , value )
15
+ import Protolude
16
+ import R1CS (toR1CS )
17
+ import System.Directory (createDirectoryIfMissing )
18
+
19
+ data GlobalOpts = GlobalOpts
20
+ { outputDir :: FilePath ,
21
+ cmd :: Command
22
+ }
23
+
24
+ optsParser :: Text -> ParserInfo GlobalOpts
25
+ optsParser progName =
26
+ info
27
+ (helper <*> globalOptsParser)
28
+ ( fullDesc
29
+ <> progDesc (" Compiling " <> Text. unpack progName <> " to a zk-SNARK" )
30
+ <> header (" Compile " <> Text. unpack progName <> " to a system of constraints and solve for a witness" )
31
+ )
32
+ where
33
+ globalOptsParser :: Parser GlobalOpts
34
+ globalOptsParser =
35
+ GlobalOpts
36
+ <$> strOption
37
+ ( long " output-dir"
38
+ <> help " output directory"
39
+ <> showDefault
40
+ <> value " circuit-output"
41
+ )
42
+ <*> hsubparser (compileCommand <> solveCommand)
43
+
44
+ compileCommand :: Mod CommandFields Command
45
+ compileCommand =
46
+ command " compile" (info (Compile <$> compileOptsParser <**> helper) (progDesc " Compile the program to an r1cs and constraint system" ))
47
+
48
+ solveCommand :: Mod CommandFields Command
49
+ solveCommand =
50
+ command " solve" (info (Solve <$> solveOptsParser <**> helper) (progDesc " Generate a witness" ))
51
+
52
+ data Command = Compile CompileOpts | Solve SolveOpts
53
+
54
+ data CompileOpts = CompileOpts
55
+ { optimizeOpts :: OptimizeOpts ,
56
+ includeJson :: Bool
57
+ }
58
+
59
+ compileOptsParser :: Parser CompileOpts
60
+ compileOptsParser =
61
+ CompileOpts
62
+ <$> optimizeOptsParser
63
+ <*> switch
64
+ ( long " json"
65
+ <> help " also write json versions of artifacts"
66
+ )
67
+
68
+ data OptimizeOpts = OptimizeOpts
69
+ { propogateConstants :: Bool ,
70
+ removeUnreachable :: Bool
71
+ }
72
+
73
+ optimizeOptsParser :: Parser OptimizeOpts
74
+ optimizeOptsParser =
75
+ OptimizeOpts
76
+ <$> switch
77
+ ( long " propogate-constants"
78
+ <> help " propogate constants through the circuit"
79
+ )
80
+ <*> switch
81
+ ( long " remove-unreachable"
82
+ <> help " detect and remove variables not contributing to the output"
83
+ )
84
+
85
+ data SolveOpts
86
+ = SolveOpts
87
+ { inputsFile :: FilePath
88
+ }
89
+
90
+ solveOptsParser :: Parser SolveOpts
91
+ solveOptsParser =
92
+ SolveOpts
93
+ <$> strOption
94
+ ( long " inputs"
95
+ <> help " inputs json file"
96
+ <> showDefault
97
+ <> value " inputs.json"
98
+ )
99
+
100
+ defaultMain ::
101
+ forall f a .
102
+ (PrimeField f ) =>
103
+ Text ->
104
+ ExprM f a ->
105
+ IO ()
106
+ defaultMain progName program = do
107
+ opts <- execParser (optsParser progName)
108
+ case cmd opts of
109
+ Compile _ -> do
110
+ let BuilderState {.. } = snd $ runCircuitBuilder program
111
+ prog = mkCircomProgram bsVars bsCircuit
112
+ r1cs = r1csToCircomR1CS $ toR1CS (cpVars prog) (cpCircuit prog)
113
+ createDirectoryIfMissing True (outputDir opts)
114
+ encodeFile (r1csFilePath $ outputDir opts) r1cs
115
+ encodeFile (binFilePath $ outputDir opts) prog
116
+ let inputsTemplate = map (const A. Null ) $ labelToVar $ cvInputsLabels $ cpVars prog
117
+ LBS. writeFile (inputsTemplateFilePath $ outputDir opts) (encodePretty $ inputsTemplate)
118
+ Solve solveOpts -> do
119
+ inputs <- do
120
+ mInputs <- decodeFileStrict (inputsFile solveOpts)
121
+ maybe (panic " Failed to decode inputs" ) (pure . map (fromInteger @ f )) mInputs
122
+ circuit <- decodeFile (binFilePath $ outputDir opts)
123
+ let wtns = nativeGenWitness circuit inputs
124
+ encodeFile (witnessFilePath $ outputDir opts) wtns
125
+ where
126
+ baseFilePath :: FilePath -> FilePath
127
+ baseFilePath dir = dir <> " /" <> Text. unpack progName
128
+ inputsTemplateFilePath dir = dir <> " /" <> " inputs-template.json"
129
+ binFilePath dir = baseFilePath dir <> " .bin"
130
+ r1csFilePath dir = baseFilePath dir <> " .r1cs"
131
+ witnessFilePath dir = baseFilePath dir <> " .wtns"
0 commit comments