Browse Source

-initial commit,

-extending flat code compiler to a more general compiler
-reducing all gates to multiplication gates only in the R1CS description of a program.
pull/8/head
mottla 5 years ago
parent
commit
705e500a5d
20 changed files with 1675 additions and 660 deletions
  1. +8
    -0
      .idea/go-snark.iml
  2. +6
    -0
      .idea/misc.xml
  3. +8
    -0
      .idea/modules.xml
  4. +6
    -0
      .idea/vcs.xml
  5. +552
    -0
      .idea/workspace.xml
  6. +8
    -6
      README.md
  7. +1
    -1
      bn128/bn128.go
  8. +1
    -1
      bn128/g1.go
  9. +1
    -1
      bn128/g2.go
  10. +382
    -0
      circuitcompiler/Programm.go
  11. +78
    -0
      circuitcompiler/Programm_test.go
  12. +310
    -135
      circuitcompiler/circuit.go
  13. +111
    -71
      circuitcompiler/circuit_test.go
  14. +46
    -8
      circuitcompiler/lexer.go
  15. +60
    -136
      circuitcompiler/parser.go
  16. +3
    -3
      cli/main.go
  17. +1
    -1
      r1csqap/r1csqap.go
  18. +1
    -1
      r1csqap/r1csqap_test.go
  19. +4
    -4
      snark.go
  20. +88
    -292
      snark_test.go

+ 8
- 0
.idea/go-snark.iml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

+ 6
- 0
.idea/misc.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

+ 8
- 0
.idea/modules.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/go-snark.iml" filepath="$PROJECT_DIR$/.idea/go-snark.iml" />
</modules>
</component>
</project>

+ 6
- 0
.idea/vcs.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

+ 552
- 0
.idea/workspace.xml

@ -0,0 +1,552 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="63e1f2c4-8982-42df-a3f2-1673aa7aec22" name="Default Changelist" comment="-initial commit,&#10;-extending flat code compiler to a more general compiler&#10;-reducing all gates to multiplication gates only in the R1CS description of a program.">
<change afterPath="$PROJECT_DIR$/.idea/go-snark.iml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/circuitcompiler/Programm.go" beforeDir="false" afterPath="$PROJECT_DIR$/circuitcompiler/Programm.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/circuitcompiler/Programm_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/circuitcompiler/Programm_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/circuitcompiler/circuit.go" beforeDir="false" afterPath="$PROJECT_DIR$/circuitcompiler/circuit.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/snark_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/snark_test.go" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/circuitcompiler/Programm.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="325">
<caret line="31" column="48" selection-start-line="31" selection-start-column="48" selection-end-line="31" selection-end-column="48" />
<folding>
<element signature="e#25#92#0" expanded="true" />
<element signature="n#!!block;n#addFunction#0" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/circuitcompiler/Programm_test.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="318">
<caret line="30" column="19" selection-start-line="30" selection-start-column="19" selection-end-line="30" selection-end-column="19" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/circuitcompiler/circuit.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="145">
<caret line="91" selection-start-line="91" selection-end-line="91" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file:///usr/local/go/src/testing/testing.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="193">
<caret line="791" selection-start-line="791" selection-end-line="791" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file:///usr/local/go/src/runtime/panic.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="185">
<caret line="512" selection-start-line="512" selection-end-line="512" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file:///usr/local/go/src/runtime/mbitmap.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="193">
<caret line="910" selection-start-line="910" selection-end-line="910" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file:///usr/local/go/src/runtime/malloc.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="193">
<caret line="932" selection-start-line="932" selection-end-line="932" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/cli/main.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="25" selection-start-line="15" selection-start-column="25" selection-end-line="15" selection-end-column="25" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/circuitcompiler/lexer.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2715">
<caret line="185" selection-start-line="185" selection-end-line="185" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/circuitcompiler/parser.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="163">
<caret line="60" selection-start-line="60" selection-end-line="60" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>arnaucube</find>
<find>oneConst</find>
<find>getmain</find>
<find>opera</find>
</findStrings>
<replaceStrings>
<replace>mottla</replace>
</replaceStrings>
</component>
<component name="GOROOT" path="/usr/local/go" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GoLibraries">
<option name="indexEntireGoPath" value="false" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/bn128/bn128.go" />
<option value="$PROJECT_DIR$/bn128/g1.go" />
<option value="$PROJECT_DIR$/bn128/g2.go" />
<option value="$PROJECT_DIR$/cli/main.go" />
<option value="$PROJECT_DIR$/r1csqap/r1csqap.go" />
<option value="$PROJECT_DIR$/r1csqap/r1csqap_test.go" />
<option value="$PROJECT_DIR$/snark.go" />
<option value="$PROJECT_DIR$/circuitcompiler/circuit_test.go" />
<option value="$PROJECT_DIR$/circuitcompiler/lexer.go" />
<option value="$PROJECT_DIR$/snark_test.go" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/circuitcompiler/parser.go" />
<option value="$PROJECT_DIR$/circuitcompiler/temp" />
<option value="$PROJECT_DIR$/circuitcompiler/Programm_test.go" />
<option value="$PROJECT_DIR$/circuitcompiler/circuit.go" />
<option value="$PROJECT_DIR$/circuitcompiler/Programm.go" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="65" />
<option name="y" value="-4" />
<option name="width" value="1855" />
<option name="height" value="1012" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="go-snark" type="b2602c69:ProjectViewProjectNode" />
<item name="go-snark" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="go-snark" type="b2602c69:ProjectViewProjectNode" />
<item name="go-snark" type="462c0819:PsiDirectoryNode" />
<item name="circuitcompiler" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="go-snark" type="b2602c69:ProjectViewProjectNode" />
<item name="go-snark" type="462c0819:PsiDirectoryNode" />
<item name="cli" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="GO_FMT" value="true" />
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="go.gopath.indexing.explicitly.defined" value="true" />
<property name="go.import.settings.migrated" value="true" />
<property name="go.sdk.automatically.set" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/circuitcompiler" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/circuitcompiler" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="go-snark" />
<working_directory value="$PROJECT_DIR$/circuitcompiler" />
<framework value="gotest" />
<kind value="PACKAGE" />
<package value="github.com/mottla/go-snark/circuitcompiler" />
<directory value="$PROJECT_DIR$/" />
<filePath value="$PROJECT_DIR$/" />
<pattern value="^TestNewProgramm$" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Go Test.TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" />
</list>
</recent_temporary>
</component>
<component name="TestHistory">
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 18h 44m 54s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 18h 53m 19s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 01m 05s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 01m 16s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 05m 45s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 05m 47s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 07m 14s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 15m 18s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 16m 13s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
<history-entry file="TestNewProgramm_in_github_com_mottla_go-snark_circuitcompiler - 2019.05.13 at 19h 17m 48s.xml">
<configuration name="TestNewProgramm in github.com/mottla/go-snark/circuitcompiler" configurationId="GoTestRunConfiguration" />
</history-entry>
</component>
<component name="TodoView" selected-index="4">
<todo-panel id="selected-file">
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="64" y="-11" width="1857" height="1020" extended-state="6" />
<layout>
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.14206745" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" weight="0.32893288" />
<window_info active="true" anchor="bottom" id="Run" order="2" visible="true" weight="0.32893288" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.39933994" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" weight="0.19031903" />
<window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Database Changes" order="8" />
<window_info anchor="bottom" id="Version Control" order="9" />
<window_info anchor="bottom" id="Terminal" order="10" />
<window_info anchor="bottom" id="Event Log" order="11" side_tool="true" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="Database" order="3" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="-initial commit,&#10;-extending flat code compiler to a more general compiler&#10;-reducing all gates to multiplication gates only in the R1CS description of a program." />
<option name="LAST_COMMIT_MESSAGE" value="-initial commit,&#10;-extending flat code compiler to a more general compiler&#10;-reducing all gates to multiplication gates only in the R1CS description of a program." />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/circuitcompiler/Programm.go</url>
<line>75</line>
<properties />
<option name="timeStamp" value="2" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/circuitcompiler/Programm.go</url>
<line>82</line>
<properties />
<option name="timeStamp" value="3" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/circuitcompiler/Programm.go</url>
<line>66</line>
<properties />
<option name="timeStamp" value="4" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/circuitcompiler/parser.go</url>
<line>60</line>
<properties />
<option name="timeStamp" value="10" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/bn128/bn128.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="90">
<caret line="6" column="25" selection-start-line="6" selection-start-column="25" selection-end-line="6" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/bn128/bn128_test.go">
<provider selected="true" editor-type-id="text-editor">
<state>
<folding>
<element signature="e#15#96#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/bn128/g1.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="16">
<caret line="5" column="25" selection-start-line="5" selection-start-column="25" selection-end-line="5" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/bn128/g1_test.go">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/bn128/g2.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="16">
<caret line="5" column="25" selection-start-line="5" selection-start-column="25" selection-end-line="5" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/bn128/g2_test.go">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/fields/fq.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-59" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/fields/fq2.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-59" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/fields/fq6.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-59" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/fields/fq12.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-59" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/fields/fqn_test.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-59" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/build-cli.sh">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/go.mod">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/go.sum">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/snark_test.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-72">
<caret line="5" column="25" selection-start-line="5" selection-start-column="25" selection-end-line="5" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/snark.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="91">
<caret line="10" column="25" selection-start-line="10" selection-start-column="25" selection-end-line="10" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/r1csqap/r1csqap_test.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-13">
<caret line="7" column="25" selection-start-line="7" selection-start-column="25" selection-end-line="7" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/r1csqap/r1csqap.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-28">
<caret line="6" column="25" selection-start-line="6" selection-start-column="25" selection-end-line="6" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/r1csqap/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor />
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="135">
<caret line="9" column="19" selection-start-line="9" selection-start-column="19" selection-end-line="9" selection-end-column="19" />
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/circuit_test.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1815">
<caret line="128" selection-start-line="128" selection-end-line="128" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/lexer.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2715">
<caret line="185" selection-start-line="185" selection-end-line="185" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/cli/main.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="25" selection-start-line="15" selection-start-column="25" selection-end-line="15" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file:///usr/local/go/src/runtime/mbitmap.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="193">
<caret line="910" selection-start-line="910" selection-end-line="910" />
</state>
</provider>
</entry>
<entry file="file:///usr/local/go/src/runtime/malloc.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="193">
<caret line="932" selection-start-line="932" selection-end-line="932" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/temp">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" selection-start-line="1" selection-end-line="1" />
</state>
</provider>
</entry>
<entry file="file:///usr/local/go/src/testing/testing.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="193">
<caret line="791" selection-start-line="791" selection-end-line="791" />
</state>
</provider>
</entry>
<entry file="file:///usr/local/go/src/runtime/panic.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="185">
<caret line="512" selection-start-line="512" selection-end-line="512" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/parser.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="163">
<caret line="60" selection-start-line="60" selection-end-line="60" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/Programm_test.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="318">
<caret line="30" column="19" selection-start-line="30" selection-start-column="19" selection-end-line="30" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/circuit.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="145">
<caret line="91" selection-start-line="91" selection-end-line="91" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/circuitcompiler/Programm.go">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="325">
<caret line="31" column="48" selection-start-line="31" selection-start-column="48" selection-end-line="31" selection-end-column="48" />
<folding>
<element signature="e#25#92#0" expanded="true" />
<element signature="n#!!block;n#addFunction#0" />
</folding>
</state>
</provider>
</entry>
</component>
</project>

+ 8
- 6
README.md

@ -7,25 +7,27 @@ zkSNARK library implementation in Go
- `Pinocchio: Nearly practical verifiable computation`, Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova https://eprint.iacr.org/2013/279.pdf
## Caution, Warning
UNDER CONSTRUCTION!
Implementation of the zkSNARK [Pinocchio protocol](https://eprint.iacr.org/2013/279.pdf) from scratch in Go to understand the concepts. Do not use in production.
Not finished, implementing this in my free time to understand it better, so I don't have much time.
This forked aims to extend its functionalities s.t. one can prove set-membership in zero knowledge.
Current implementation status:
- [x] Finite Fields (1, 2, 6, 12) operations
- [x] G1 and G2 curve operations
- [x] BN128 Pairing
- [x] BN128 Pairing (to be replaced with less unsecure curve)
- [x] circuit code compiler
- [ ] code to flat code (improve circuit compiler)
- [ ] code to flat code (improve circuit compiler) (in progress)
- [x] flat code compiler
- [x] circuit to R1CS
- [x] circuit to R1CS with gate reduction optimisation
- [x] polynomial operations
- [x] R1CS to QAP
- [x] generate trusted setup
- [x] generate proofs
- [x] verify proofs with BN128 pairing
- [ ] move witness calculation outside the setup phase
- [ ] Groth16
- [x] move witness calculation outside the setup phase
- [ ] Groth16 (in progress)
- [ ] multiple optimizations

+ 1
- 1
bn128/bn128.go

@ -4,7 +4,7 @@ import (
"errors"
"math/big"
"github.com/arnaucube/go-snark/fields"
"github.com/mottla/go-snark/fields"
)
// Bn128 is the data structure of the BN128

+ 1
- 1
bn128/g1.go

@ -3,7 +3,7 @@ package bn128
import (
"math/big"
"github.com/arnaucube/go-snark/fields"
"github.com/mottla/go-snark/fields"
)
type G1 struct {

+ 1
- 1
bn128/g2.go

@ -3,7 +3,7 @@ package bn128
import (
"math/big"
"github.com/arnaucube/go-snark/fields"
"github.com/mottla/go-snark/fields"
)
type G2 struct {

+ 382
- 0
circuitcompiler/Programm.go

@ -0,0 +1,382 @@
package circuitcompiler
import (
"fmt"
"github.com/mottla/go-snark/r1csqap"
"math/big"
)
type Program struct {
functions map[string]*Circuit
signals []string
globalInputs []*Constraint
R1CS struct {
A [][]*big.Int
B [][]*big.Int
C [][]*big.Int
}
}
func (p *Program) PrintContraintTrees() {
for k, v := range p.functions {
fmt.Println(k)
PrintTree(v.root)
}
}
func (p *Program) BuildConstraintTrees() {
functionRootMap := make(map[string]*gate)
for _, circuit := range p.functions {
circuit.addConstraint(p.oneConstraint())
fName := composeNewFunction(circuit.Name, circuit.Inputs)
root := &gate{value: circuit.constraintMap[fName]}
functionRootMap[fName] = root
circuit.root = root
}
for _, circuit := range p.functions {
buildTree(circuit.constraintMap, circuit.root)
}
return
}
func buildTree(con map[string]*Constraint, g *gate) {
if _, ex := con[g.value.Out]; ex {
if g.OperationType()&(IN|CONST) != 0 {
return
}
} else {
panic(fmt.Sprintf("undefined variable %s", g.value.Out))
}
if g.OperationType() == FUNC {
g.funcInputs = []*gate{}
for _, in := range g.value.Inputs {
if constr, ex := con[in]; ex {
newGate := &gate{value: constr}
g.funcInputs = append(g.funcInputs, newGate)
buildTree(con, newGate)
} else {
panic(fmt.Sprintf("undefined value %s", g.value.V1))
}
}
return
}
if constr, ex := con[g.value.V1]; ex {
g.addLeft(constr)
buildTree(con, g.left)
} else {
panic(fmt.Sprintf("undefined value %s", g.value.V1))
}
if constr, ex := con[g.value.V2]; ex {
g.addRight(constr)
buildTree(con, g.right)
} else {
panic(fmt.Sprintf("undefined value %s", g.value.V2))
}
}
func (p *Program) ReduceCombinedTree() (orderedmGates []gate) {
mGatesUsed := make(map[string]bool)
orderedmGates = []gate{}
functionRootMap := make(map[string]*gate)
for k, v := range p.functions {
functionRootMap[k] = v.root
}
functionRenamer := func(c *Constraint) *gate {
if c.Op != FUNC {
panic("not a function")
}
if b, name, in := isFunction(c.Out); b {
if k, v := p.functions[name]; v {
//fmt.Println("unrenamed thing")
//PrintTree(k.root)
k.renameInputs(in)
//fmt.Println("renamed thing")
//PrintTree(k.root)
return k.root
}
} else {
panic("not a function dude")
}
return nil
}
traverseCombinedMultiplicationGates(p.getMainCircut().root, mGatesUsed, &orderedmGates, functionRootMap, functionRenamer, false, false)
//for _, g := range mGates {
// orderedmGates[len(orderedmGates)-1-g.index] = g
//}
return orderedmGates
}
func traverseCombinedMultiplicationGates(root *gate, mGatesUsed map[string]bool, orderedmGates *[]gate, functionRootMap map[string]*gate, functionRenamer func(c *Constraint) *gate, negate bool, inverse bool) {
//if root == nil {
// return
//}
if root.OperationType() == FUNC {
//if a input has already been built, we let this subroutine know
newMap := make(map[string]bool)
for _, in := range root.funcInputs {
if _, ex := mGatesUsed[in.value.Out]; ex {
newMap[in.value.Out] = true
} else {
traverseCombinedMultiplicationGates(in, mGatesUsed, orderedmGates, functionRootMap, functionRenamer, negate, inverse)
}
}
//mGatesUsed[root.value.Out] = true
traverseCombinedMultiplicationGates(functionRenamer(root.value), newMap, orderedmGates, functionRootMap, functionRenamer, negate, inverse)
} else {
if _, alreadyComputed := mGatesUsed[root.value.V1]; !alreadyComputed && root.OperationType()&(IN|CONST) == 0 {
traverseCombinedMultiplicationGates(root.left, mGatesUsed, orderedmGates, functionRootMap, functionRenamer, negate, inverse)
}
if _, alreadyComputed := mGatesUsed[root.value.V2]; !alreadyComputed && root.OperationType()&(IN|CONST) == 0 {
traverseCombinedMultiplicationGates(root.right, mGatesUsed, orderedmGates, functionRootMap, functionRenamer, Xor(negate, root.value.negate), Xor(inverse, root.value.invert))
}
}
if root.OperationType() == MULTIPLY {
root.leftIns = make(map[string]int)
collectAtomsInSubtree(root.left, root.leftIns, functionRootMap, negate, inverse)
root.rightIns = make(map[string]int)
collectAtomsInSubtree(root.right, root.rightIns, functionRootMap, Xor(negate, root.value.negate), Xor(inverse, root.value.invert))
root.index = len(mGatesUsed)
mGatesUsed[root.value.Out] = true
rootGate := cloneGate(root)
*orderedmGates = append(*orderedmGates, *rootGate)
}
//TODO optimize if output is not a multipication gate
}
//copies a gate neglecting its references to other gates
func cloneGate(in *gate) (out *gate) {
constr := &Constraint{Inputs: in.value.Inputs, Out: in.value.Out, Op: in.value.Op, invert: in.value.invert, negate: in.value.negate, V2: in.value.V2, V1: in.value.V1}
nRightins := make(map[string]int)
nLeftInst := make(map[string]int)
for k, v := range in.rightIns {
nRightins[k] = v
}
for k, v := range in.leftIns {
nLeftInst[k] = v
}
return &gate{value: constr, leftIns: nLeftInst, rightIns: nRightins, index: in.index}
}
func (p *Program) getMainCircut() *Circuit {
return p.functions["main"]
}
func (p *Program) addGlobalInput(c *Constraint) {
p.globalInputs = append(p.globalInputs, c)
}
func NewProgramm() *Program {
return &Program{functions: map[string]*Circuit{}, signals: []string{}, globalInputs: []*Constraint{{Op: CONST, Out: "one"}}}
}
func (p *Program) oneConstraint() *Constraint {
if p.globalInputs[0].Out != "one" {
panic("'one' should be first global input")
}
return p.globalInputs[0]
}
func (p *Program) addSignal(name string) {
p.signals = append(p.signals, name)
}
func (p *Program) addFunction(constraint *Constraint) (c *Circuit) {
name := constraint.Out
fmt.Println("try to add function ", name)
b, name2, _ := isFunction(name)
if !b {
panic(fmt.Sprintf("not a function: %v", constraint))
}
name = name2
if _, ex := p.functions[name]; ex {
panic("function already declared")
}
c = newCircuit(name)
p.functions[name] = c
//if constraint.Literal == "main" {
for _, in := range constraint.Inputs {
newConstr := &Constraint{
Op: IN,
Out: in,
}
if name == "main" {
p.addGlobalInput(newConstr)
}
c.addConstraint(newConstr)
}
c.Inputs = constraint.Inputs
return
}
// GenerateR1CS generates the R1CS polynomials from the Circuit
func (p *Program) GenerateReducedR1CS(mGates []gate) (a, b, c [][]*big.Int) {
// from flat code to R1CS
offset := len(p.globalInputs)
// one + in1 +in2+... + gate1 + gate2 .. + out
size := offset + len(mGates)
indexMap := make(map[string]int)
//circ.Signals = []string{"one"}
for i, v := range p.globalInputs {
indexMap[v.Out] = i
//circ.Signals = append(circ.Signals, v)
}
for i, v := range mGates {
indexMap[v.value.Out] = i + offset
//circ.Signals = append(circ.Signals, v.value.Out)
}
//circ.NVars = len(circ.Signals)
//circ.NSignals = len(circ.Signals)
for _, gate := range mGates {
if gate.OperationType() == MULTIPLY {
aConstraint := r1csqap.ArrayOfBigZeros(size)
bConstraint := r1csqap.ArrayOfBigZeros(size)
cConstraint := r1csqap.ArrayOfBigZeros(size)
for leftInput, val := range gate.leftIns {
insertVar3(aConstraint, val, leftInput, indexMap[leftInput])
}
for rightInput, val := range gate.rightIns {
insertVar3(bConstraint, val, rightInput, indexMap[rightInput])
}
cConstraint[indexMap[gate.value.Out]] = big.NewInt(int64(1))
if gate.value.invert {
a = append(a, cConstraint)
b = append(b, bConstraint)
c = append(c, aConstraint)
} else {
a = append(a, aConstraint)
b = append(b, bConstraint)
c = append(c, cConstraint)
}
} else {
panic("not a m gate")
}
}
p.R1CS.A = a
p.R1CS.B = b
p.R1CS.C = c
return a, b, c
}
func insertVar3(arr []*big.Int, val int, input string, index int) {
isVal, value := isValue(input)
var valueBigInt *big.Int
if isVal {
valueBigInt = big.NewInt(int64(value))
arr[0] = new(big.Int).Add(arr[0], valueBigInt)
} else {
//if !indexMap[leftInput] {
// panic(errors.New("using variable before it's set"))
//}
valueBigInt = big.NewInt(int64(val))
arr[index] = new(big.Int).Add(arr[index], valueBigInt)
}
}
func (p *Program) CalculateWitness(input []*big.Int) (witness []*big.Int) {
if len(p.globalInputs)-1 != len(input) {
panic("input do not match the required inputs")
}
witness = r1csqap.ArrayOfBigZeros(len(p.R1CS.A[0]))
set := make([]bool, len(witness))
witness[0] = big.NewInt(int64(1))
set[0] = true
for i := range input {
witness[i+1] = input[i]
set[i+1] = true
}
zero := big.NewInt(int64(0))
for i := 0; i < len(p.R1CS.A); i++ {
gatesLeftInputs := p.R1CS.A[i]
gatesRightInputs := p.R1CS.B[i]
gatesOutputs := p.R1CS.C[i]
sumLeft := big.NewInt(int64(0))
sumRight := big.NewInt(int64(0))
sumOut := big.NewInt(int64(0))
index := -1
division := false
for j, val := range gatesLeftInputs {
if val.Cmp(zero) != 0 {
if !set[j] {
index = j
division = true
break
}
sumLeft.Add(sumLeft, new(big.Int).Mul(val, witness[j]))
}
}
for j, val := range gatesRightInputs {
if val.Cmp(zero) != 0 {
sumRight.Add(sumRight, new(big.Int).Mul(val, witness[j]))
}
}
for j, val := range gatesOutputs {
if val.Cmp(zero) != 0 {
if !set[j] {
if index != -1 {
panic("invalid R1CS form")
}
index = j
break
}
sumOut.Add(sumOut, new(big.Int).Mul(val, witness[j]))
}
}
if !division {
set[index] = true
witness[index] = new(big.Int).Mul(sumLeft, sumRight)
} else {
b := sumRight.Int64()
c := sumOut.Int64()
set[index] = true
witness[index] = big.NewInt(c / b)
}
}
return
}

+ 78
- 0
circuitcompiler/Programm_test.go

@ -0,0 +1,78 @@
package circuitcompiler
import (
"fmt"
"math/big"
"strings"
"testing"
)
func TestProgramm_BuildConstraintTree(t *testing.T) {
line := "asdf asfd"
line = strings.TrimFunc(line, func(i rune) bool { return isWhitespace(i) })
fmt.Println(line)
}
func TestNewProgramm(t *testing.T) {
flat := `
func do(x):
e = x * x
b = e * e
c = b * b
d = c * c
out = d * 1
func add(x ,k):
z = k * x
out = do(x) + mul(x,z)
func main(a,b):
out = do(5) + 4
func mul(a,b):
out = a * b
`
//flat := `
//func do(x):
// b = x - 2
// out = x * b
//func main(a,b):
// out = do(a) + 4
//`
parser := NewParser(strings.NewReader(flat))
program, err := parser.Parse()
if err != nil {
panic(err)
}
fmt.Println("\n unreduced")
fmt.Println(flat)
program.BuildConstraintTrees()
for k, v := range program.functions {
fmt.Println(k)
PrintTree(v.root)
}
fmt.Println("\nReduced gates")
//PrintTree(froots["mul"])
gates := program.ReduceCombinedTree()
for _, g := range gates {
fmt.Println(g)
}
fmt.Println("generating R1CS")
a, b, c := program.GenerateReducedR1CS(gates)
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
a1 := big.NewInt(int64(6))
a2 := big.NewInt(int64(5))
inputs := []*big.Int{a1, a2}
w := program.CalculateWitness(inputs)
fmt.Println("witness")
fmt.Println(w)
}

+ 310
- 135
circuitcompiler/circuit.go

@ -2,10 +2,11 @@ package circuitcompiler
import (
"errors"
"fmt"
"math/big"
"regexp"
"strconv"
"github.com/arnaucube/go-snark/r1csqap"
"strings"
)
// Circuit is the data structure of the compiled circuit
@ -13,174 +14,348 @@ type Circuit struct {
NVars int
NPublic int
NSignals int
PrivateInputs []string
PublicInputs []string
Inputs []string
Signals []string
PublicSignals []string
Witness []*big.Int
Constraints []Constraint
R1CS struct {
Name string
root *gate
//after reducing
constraintMap map[string]*Constraint
//used map[string]bool
R1CS struct {
A [][]*big.Int
B [][]*big.Int
C [][]*big.Int
}
}
type gate struct {
index int
left *gate
right *gate
funcInputs []*gate
value *Constraint
leftIns map[string]int //leftIns and RightIns after addition gates have been reduced. only multiplication gates remain
rightIns map[string]int
}
func (g gate) String() string {
return fmt.Sprintf("Gate %v : %v with left %v right %v", g.index, g.value, g.leftIns, g.rightIns)
}
//type variable struct {
// val string
//}
// Constraint is the data structure of a flat code operation
type Constraint struct {
// v1 op v2 = out
Op string
V1 string
V2 string
Out string
Literal string
Op Token
V1 string
V2 string
Out string
//fV1 *variable
//fV2 *variable
//fOut *variable
//Literal string
Inputs []string // in func declaration case
//fInputs []*variable
negate bool
invert bool
}
PrivateInputs []string // in func declaration case
PublicInputs []string // in func declaration case
func (c Constraint) String() string {
if c.negate || c.invert {
return fmt.Sprintf("|%v = %v %v %v| negated: %v, inverted %v", c.Out, c.V1, c.Op, c.V2, c.negate, c.invert)
}
return fmt.Sprintf("|%v = %v %v %v|", c.Out, c.V1, c.Op, c.V2)
}
func indexInArray(arr []string, e string) int {
for i, a := range arr {
if a == e {
return i
func newCircuit(name string) *Circuit {
return &Circuit{Name: name, constraintMap: make(map[string]*Constraint)}
}
func (g *gate) addLeft(c *Constraint) {
if g.left != nil {
panic("already set left gate")
}
g.left = &gate{value: c}
}
func (g *gate) addRight(c *Constraint) {
if g.right != nil {
panic("already set left gate")
}
g.right = &gate{value: c}
}
func (circ *Circuit) addConstraint(constraint *Constraint) {
if _, ex := circ.constraintMap[constraint.Out]; ex {
panic("already used FlatConstraint")
}
if constraint.Op == DIVIDE {
constraint.Op = MULTIPLY
constraint.invert = true
} else if constraint.Op == MINUS {
constraint.Op = PLUS
constraint.negate = true
}
//todo this is dangerous.. if someone would use out as variable name, things would be fucked
if constraint.Out == "out" {
constraint.Out = composeNewFunction(circ.Name, circ.Inputs)
if circ.Name == "main" {
//the main functions output must be a multiplication gate
//if its not, then we simple create one where outNew = 1 * outOld
if constraint.Op&(MINUS|PLUS) != 0 {
newOut := &Constraint{Out: constraint.Out, V1: "one", V2: "out2", Op: MULTIPLY}
delete(circ.constraintMap, constraint.Out)
circ.addConstraint(newOut)
constraint.Out = "out2"
circ.addConstraint(constraint)
}
}
}
return -1
addConstantsAndFunctions := func(constraint string) {
if b, _ := isValue(constraint); b {
circ.constraintMap[constraint] = &Constraint{Op: CONST, Out: constraint}
} else if b, _, inputs := isFunction(constraint); b {
//check if function input is a constant like foo(a,4)
for _, in := range inputs {
if b, _ := isValue(in); b {
circ.constraintMap[in] = &Constraint{Op: CONST, Out: in}
}
}
circ.constraintMap[constraint] = &Constraint{Op: FUNC, Out: constraint, Inputs: inputs}
}
}
addConstantsAndFunctions(constraint.V1)
addConstantsAndFunctions(constraint.V2)
circ.constraintMap[constraint.Out] = constraint
}
func isValue(a string) (bool, int) {
v, err := strconv.Atoi(a)
if err != nil {
return false, 0
func (circ *Circuit) renameInputs(inputs []string) {
if len(inputs) != len(circ.Inputs) {
panic("given inputs != circuit.Inputs")
}
return true, v
mapping := make(map[string]string)
for i := 0; i < len(inputs); i++ {
if _, ex := circ.constraintMap[inputs[i]]; ex {
//this is a tricky part. So we replace former inputs with the new ones, thereby
//it might be, that the new input name has already been used for some output inside the function
//currently I dont know an elegant way how to handle this renaming issue
if circ.constraintMap[inputs[i]].Op != IN {
panic(fmt.Sprintf("renaming collsion with %s", inputs[i]))
}
}
mapping[circ.Inputs[i]] = inputs[i]
}
//fmt.Println(mapping)
circ.Inputs = inputs
permute := func(in string) string {
if out, ex := mapping[in]; ex {
return out
}
return in
}
permuteListe := func(in []string) []string {
for i := 0; i < len(in); i++ {
in[i] = permute(in[i])
}
return in
}
for _, constraint := range circ.constraintMap {
if constraint.Op == IN {
constraint.Out = permute(constraint.Out)
continue
}
if b, n, in := isFunction(constraint.Out); b {
constraint.Out = composeNewFunction(n, permuteListe(in))
constraint.Inputs = permuteListe(in)
}
if b, n, in := isFunction(constraint.V1); b {
constraint.V1 = composeNewFunction(n, permuteListe(in))
constraint.Inputs = permuteListe(in)
}
if b, n, in := isFunction(constraint.V2); b {
constraint.V2 = composeNewFunction(n, permuteListe(in))
constraint.Inputs = permuteListe(in)
}
constraint.V1 = permute(constraint.V1)
constraint.V2 = permute(constraint.V2)
}
return
}
func insertVar(arr []*big.Int, signals []string, v string, used map[string]bool) ([]*big.Int, map[string]bool) {
isVal, value := isValue(v)
valueBigInt := big.NewInt(int64(value))
if isVal {
arr[0] = new(big.Int).Add(arr[0], valueBigInt)
func composeNewFunction(fname string, inputs []string) string {
builder := strings.Builder{}
builder.WriteString(fname)
builder.WriteRune('(')
for i := 0; i < len(inputs); i++ {
builder.WriteString(inputs[i])
if i < len(inputs)-1 {
builder.WriteRune(',')
}
}
builder.WriteRune(')')
return builder.String()
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func TreeDepth(g *gate) int {
return printDepth(g, 0)
}
func printDepth(g *gate, d int) int {
d = d + 1
if g.left != nil && g.right != nil {
return max(printDepth(g.left, d), printDepth(g.right, d))
} else if g.left != nil {
return printDepth(g.left, d)
} else if g.right != nil {
return printDepth(g.right, d)
}
return d
}
func CountMultiplicationGates(g *gate) int {
if g == nil {
return 0
}
if len(g.rightIns) > 0 || len(g.leftIns) > 0 {
return 1 + CountMultiplicationGates(g.left) + CountMultiplicationGates(g.right)
} else {
if !used[v] {
panic(errors.New("using variable before it's set"))
return CountMultiplicationGates(g.left) + CountMultiplicationGates(g.right)
}
return 0
}
//TODO avoid printing multiple times in case of loops
func PrintTree(g *gate) {
printTree(g, 0)
}
func printTree(g *gate, d int) {
d += 1
if g.leftIns == nil || g.rightIns == nil {
fmt.Printf("Depth: %v - %s \t \t \t \t \n", d, g.value)
} else {
fmt.Printf("Depth: %v - %s \t \t \t \t with l %v and r %v\n", d, g.value, g.leftIns, g.rightIns)
}
if g.funcInputs != nil {
for _, v := range g.funcInputs {
printTree(v, d)
}
arr[indexInArray(signals, v)] = new(big.Int).Add(arr[indexInArray(signals, v)], big.NewInt(int64(1)))
}
return arr, used
if g.left != nil {
printTree(g.left, d)
}
if g.right != nil {
printTree(g.right, d)
}
}
func insertVarNeg(arr []*big.Int, signals []string, v string, used map[string]bool) ([]*big.Int, map[string]bool) {
isVal, value := isValue(v)
valueBigInt := big.NewInt(int64(value))
if isVal {
arr[0] = new(big.Int).Add(arr[0], valueBigInt)
func addToMap(value string, in map[string]int, negate bool) {
if negate {
in[value] = in[value] - 1
} else {
if !used[v] {
panic(errors.New("using variable before it's set"))
in[value] = in[value] + 1
}
}
func collectAtomsInSubtree(g *gate, in map[string]int, functionRootMap map[string]*gate, negate bool, invert bool) {
if g == nil {
return
}
if g.OperationType()&(MULTIPLY|IN|CONST) != 0 {
addToMap(g.value.Out, in, negate)
return
}
if g.OperationType() == FUNC {
if b, name, _ := isFunction(g.value.Out); b {
collectAtomsInSubtree(functionRootMap[name], in, functionRootMap, negate, invert)
} else {
panic("function expected")
}
arr[indexInArray(signals, v)] = new(big.Int).Add(arr[indexInArray(signals, v)], big.NewInt(int64(-1)))
}
return arr, used
}
// GenerateR1CS generates the R1CS polynomials from the Circuit
func (circ *Circuit) GenerateR1CS() ([][]*big.Int, [][]*big.Int, [][]*big.Int) {
// from flat code to R1CS
var a [][]*big.Int
var b [][]*big.Int
var c [][]*big.Int
used := make(map[string]bool)
for _, constraint := range circ.Constraints {
aConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
bConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
cConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
// if existInArray(constraint.Out) {
// if used[constraint.Out] {
// panic(errors.New("out variable already used: " + constraint.Out))
// }
used[constraint.Out] = true
if constraint.Op == "in" {
for i := 0; i <= len(circ.PublicInputs); i++ {
aConstraint[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Add(aConstraint[indexInArray(circ.Signals, constraint.Out)], big.NewInt(int64(1)))
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.Out, used)
bConstraint[0] = big.NewInt(int64(1))
}
continue
} else if constraint.Op == "+" {
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V1, used)
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V2, used)
bConstraint[0] = big.NewInt(int64(1))
} else if constraint.Op == "-" {
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
aConstraint, used = insertVarNeg(aConstraint, circ.Signals, constraint.V1, used)
aConstraint, used = insertVarNeg(aConstraint, circ.Signals, constraint.V2, used)
bConstraint[0] = big.NewInt(int64(1))
} else if constraint.Op == "*" {
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V1, used)
bConstraint, used = insertVar(bConstraint, circ.Signals, constraint.V2, used)
} else if constraint.Op == "/" {
cConstraint, used = insertVar(cConstraint, circ.Signals, constraint.V1, used)
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
bConstraint, used = insertVar(bConstraint, circ.Signals, constraint.V2, used)
}
collectAtomsInSubtree(g.left, in, functionRootMap, negate, invert)
collectAtomsInSubtree(g.right, in, functionRootMap, Xor(negate, g.value.negate), invert)
}
func Xor(a, b bool) bool {
return (a && !b) || (!a && b)
}
func (g *gate) ExtractValues(in []int) (er error) {
if b, v1 := isValue(g.value.V1); b {
if b2, v2 := isValue(g.value.V2); b2 {
in = append(in, v1, v2)
return nil
}
}
return errors.New(fmt.Sprintf("Gate \"%s\" has no int values", g.value))
}
a = append(a, aConstraint)
b = append(b, bConstraint)
c = append(c, cConstraint)
func (g *gate) OperationType() Token {
return g.value.Op
}
//returns index of e if its in arr
//return -1 if e not in arr
func indexInArray(arr []string, e string) int {
for i, a := range arr {
if a == e {
return i
}
}
panic("lul")
return -1
}
func isValue(a string) (bool, int) {
v, err := strconv.Atoi(a)
if err != nil {
return false, 0
}
circ.R1CS.A = a
circ.R1CS.B = b
circ.R1CS.C = c
return a, b, c
return true, v
}
func isFunction(a string) (tf bool, name string, inputs []string) {
func grabVar(signals []string, w []*big.Int, vStr string) *big.Int {
isVal, v := isValue(vStr)
vBig := big.NewInt(int64(v))
if isVal {
return vBig
} else {
return w[indexInArray(signals, vStr)]
if !strings.ContainsRune(a, '(') && !strings.ContainsRune(a, ')') {
return false, "", nil
}
name = strings.Split(a, "(")[0]
// read string inside ( )
rgx := regexp.MustCompile(`\((.*?)\)`)
insideParenthesis := rgx.FindStringSubmatch(a)
varsString := strings.Replace(insideParenthesis[1], " ", "", -1)
inputs = strings.Split(varsString, ",")
return true, name, inputs
}
type Inputs struct {
Private []*big.Int
Public []*big.Int
}
// CalculateWitness calculates the Witness of a Circuit based on the given inputs
// witness = [ one, output, publicInputs, privateInputs, ...]
func (circ *Circuit) CalculateWitness(privateInputs []*big.Int, publicInputs []*big.Int) ([]*big.Int, error) {
if len(privateInputs) != len(circ.PrivateInputs) {
return []*big.Int{}, errors.New("given privateInputs != circuit.PublicInputs")
}
if len(publicInputs) != len(circ.PublicInputs) {
return []*big.Int{}, errors.New("given publicInputs != circuit.PublicInputs")
}
w := r1csqap.ArrayOfBigZeros(len(circ.Signals))
w[0] = big.NewInt(int64(1))
for i, input := range publicInputs {
w[i+1] = input
}
for i, input := range privateInputs {
w[i+len(publicInputs)+1] = input
}
for _, constraint := range circ.Constraints {
if constraint.Op == "in" {
} else if constraint.Op == "+" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Add(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
} else if constraint.Op == "-" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Sub(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
} else if constraint.Op == "*" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Mul(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
} else if constraint.Op == "/" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Div(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
}
}
return w, nil
Publics []*big.Int
}

+ 111
- 71
circuitcompiler/circuit_test.go

@ -1,88 +1,128 @@
package circuitcompiler
import (
"math/big"
"strings"
//"fmt"
////"math/big"
//"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestXor(t *testing.T) {
assert.Equal(t, false, Xor(true, true))
assert.Equal(t, true, Xor(true, false))
assert.Equal(t, true, Xor(false, true))
assert.Equal(t, false, Xor(false, false))
}
func TestCircuitParser(t *testing.T) {
// y = x^3 + x + 5
flat := `
func test(private s0, public s1):
s2 = s0 * s0
s3 = s2 * s0
s4 = s3 + s0
s5 = s4 + 5
equals(s1, s5)
out = 1 * 1
`
parser := NewParser(strings.NewReader(flat))
circuit, err := parser.Parse()
assert.Nil(t, err)
// flat code to R1CS
a, b, c := circuit.GenerateR1CS()
assert.Equal(t, "s0", circuit.PrivateInputs[0])
assert.Equal(t, "s1", circuit.PublicInputs[0])
//flat := `
//func main(a,b):
// c = a / b
// d = c * b
// e = d - c
// f = c * 55
// out = f / e
//`
//flat := `
//func test(a,b):
// d = a / b
// c = a + d
// f = a * 55
// g = a / d
// h = g + f
// i = c + h
// out = i / c
//`
//parser := NewParser(strings.NewReader(flat))
//programm, err := parser.Parse()
//circuit := programm.getMainCircut()
//assert.Nil(t, err)
//fmt.Println("\n unreduced")
//fmt.Println(flat)
assert.Equal(t, []string{"one", "s1", "s0", "s2", "s3", "s4", "s5", "out"}, circuit.Signals)
//fmt.Println("generating R1CS from flat code")
//a, b, c := circuit.GenerateR1CS()
//fmt.Println(a)
//fmt.Println(b)
//fmt.Println(c)
//
//a1 := big.NewInt(int64(6))
//a2 := big.NewInt(int64(5))
//inputs := []*big.Int{a1, a2}
//// Calculate Witness
//w, err := circuit.CalculateWitness(inputs)
//assert.Nil(t, err)
//fmt.Println("w", w)
//fmt.Printf("inputs %s", circuit.Inputs)
//fmt.Printf("signals %s", circuit.Signals)
// expected result
b0 := big.NewInt(int64(0))
b1 := big.NewInt(int64(1))
b5 := big.NewInt(int64(5))
aExpected := [][]*big.Int{
[]*big.Int{b0, b0, b1, b0, b0, b0, b0, b0},
[]*big.Int{b0, b0, b0, b1, b0, b0, b0, b0},
[]*big.Int{b0, b0, b1, b0, b1, b0, b0, b0},
[]*big.Int{b5, b0, b0, b0, b0, b1, b0, b0},
[]*big.Int{b0, b0, b0, b0, b0, b0, b1, b0},
[]*big.Int{b0, b1, b0, b0, b0, b0, b0, b0},
[]*big.Int{b1, b0, b0, b0, b0, b0, b0, b0},
}
bExpected := [][]*big.Int{
[]*big.Int{b0, b0, b1, b0, b0, b0, b0, b0},
[]*big.Int{b0, b0, b1, b0, b0, b0, b0, b0},
[]*big.Int{b1, b0, b0, b0, b0, b0, b0, b0},
[]*big.Int{b1, b0, b0, b0, b0, b0, b0, b0},
[]*big.Int{b1, b0, b0, b0, b0, b0, b0, b0},
[]*big.Int{b1, b0, b0, b0, b0, b0, b0, b0},
[]*big.Int{b1, b0, b0, b0, b0, b0, b0, b0},
}
cExpected := [][]*big.Int{
[]*big.Int{b0, b0, b0, b1, b0, b0, b0, b0},
[]*big.Int{b0, b0, b0, b0, b1, b0, b0, b0},
[]*big.Int{b0, b0, b0, b0, b0, b1, b0, b0},
[]*big.Int{b0, b0, b0, b0, b0, b0, b1, b0},
[]*big.Int{b0, b1, b0, b0, b0, b0, b0, b0},
[]*big.Int{b0, b0, b0, b0, b0, b0, b1, b0},
[]*big.Int{b0, b0, b0, b0, b0, b0, b0, b1},
}
//
//fmt.Println("Reduced Tree Parsing")
//r := circuit.BuildConstraintTree()
//constraintReduced := ReduceTree(r)
//fmt.Printf("depth %v, mGates %v \n", printDepth(r, 0), CountMultiplicationGates(r))
//printTree(r, 0)
//
//a,b,c,w = circuit.GenerateReducedR1CSandWitness(inputs,constraintReduced)
//fmt.Println("\ngenerating R1CS from reduced flat code")
//fmt.Println("\nR1CS:")
//fmt.Println("a:", a)
//fmt.Println("b:", b)
//fmt.Println("c:", c)
//fmt.Println("w", w)
assert.Equal(t, aExpected, a)
assert.Equal(t, bExpected, b)
assert.Equal(t, cExpected, c)
//// R1CS to QAP
//alphas, betas, gammas, zxQAP := fields.R1CSToQAP(a, b, c)
//fmt.Println("qap")
//fmt.Println("alphas", len(alphas))
//fmt.Println("alphas", alphas[0])
//fmt.Println("betas", len(betas))
//fmt.Println("gammas", len(gammas))
//fmt.Println("zx length", len(zxQAP))
//circuit.reduceAdditionGates()
//
//fmt.Println(circuit)
//fmt.Println("generating R1CS from flat code")
//a, b, c = circuit.GenerateR1CS()
//
//fmt.Println(a)
//fmt.Println(b)
//fmt.Println(c)
//
//// Calculate Witness
//w, err = circuit.CalculateWitness(inputs)
//assert.Nil(t, err)
//fmt.Println("w", w)
b3 := big.NewInt(int64(3))
privateInputs := []*big.Int{b3}
b35 := big.NewInt(int64(35))
publicInputs := []*big.Int{b35}
// Calculate Witness
w, err := circuit.CalculateWitness(privateInputs, publicInputs)
assert.Nil(t, err)
b9 := big.NewInt(int64(9))
b27 := big.NewInt(int64(27))
b30 := big.NewInt(int64(30))
wExpected := []*big.Int{b1, b35, b3, b9, b27, b30, b35, b1}
assert.Equal(t, wExpected, w)
//// expected result
//b0 := big.NewInt(int64(0))
//b1 := big.NewInt(int64(1))
//b5 := big.NewInt(int64(5))
//aExpected := [][]*big.Int{
// []*big.Int{b0, b0, b1, b0, b0, b0},
// []*big.Int{b0, b0, b0, b1, b0, b0},
// []*big.Int{b0, b0, b1, b0, b1, b0},
// []*big.Int{b5, b0, b0, b0, b0, b1},
//}
//bExpected := [][]*big.Int{
// []*big.Int{b0, b0, b1, b0, b0, b0},
// []*big.Int{b0, b0, b1, b0, b0, b0},
// []*big.Int{b1, b0, b0, b0, b0, b0},
// []*big.Int{b1, b0, b0, b0, b0, b0},
//}
//cExpected := [][]*big.Int{
// []*big.Int{b0, b0, b0, b1, b0, b0},
// []*big.Int{b0, b0, b0, b0, b1, b0},
// []*big.Int{b0, b0, b0, b0, b0, b1},
// []*big.Int{b0, b1, b0, b0, b0, b0},
//}
// circuitJson, _ := json.Marshal(circuit)
// fmt.Println("circuit:", string(circuitJson))
//assert.Equal(t, aExpected, a)
//assert.Equal(t, bExpected, b)
//assert.Equal(t, cExpected, c)
assert.Equal(t, circuit.NPublic, 1)
assert.Equal(t, len(circuit.PublicInputs), 1)
assert.Equal(t, len(circuit.PrivateInputs), 1)
}

+ 46
- 8
circuitcompiler/lexer.go

@ -10,25 +10,50 @@ type OperatorSymbol int
type Token int
const (
ILLEGAL Token = iota
ILLEGAL Token = 1 << iota
WS
EOF
FUNC
IDENT // val
VAR // var
CONST // const value
IN
VAR // var
CONST // const value
EQ // =
PLUS // +
MINUS // -
MULTIPLY // *
DIVIDE // /
EXP // ^
OUT
)
func (ch Token) String() string {
switch ch {
case EQ:
return "="
case PLUS:
return "+"
case MINUS:
return "-"
case MULTIPLY:
return "*"
case DIVIDE:
return "/"
case EXP:
return "^"
case FUNC:
return "func"
case IN:
return "In"
case CONST:
return "Const"
default:
return "unknown Token"
}
}
var eof = rune(0)
func isWhitespace(ch rune) bool {
@ -96,6 +121,8 @@ func (s *Scanner) scan() (tok Token, lit string) {
return DIVIDE, "/"
case '^':
return EXP, "^"
//case '(':
// return EXP, "func"
}
return ILLEGAL, string(ch)
@ -121,10 +148,20 @@ func (s *Scanner) scanWhitespace() (token Token, lit string) {
func (s *Scanner) scanIndent() (tok Token, lit string) {
var buf bytes.Buffer
buf.WriteRune(s.read())
tok = IDENT
for {
if ch := s.read(); ch == eof {
break
} else if ch == '(' {
tok = FUNC
_, _ = buf.WriteRune(ch)
} else if ch == ',' && tok == FUNC {
_, _ = buf.WriteRune(ch)
} else if isWhitespace(ch) && tok == FUNC {
continue
} else if ch == ')' && tok == FUNC {
_, _ = buf.WriteRune(ch)
break
} else if !isLetter(ch) && !isDigit(ch) {
s.unread()
break
@ -132,6 +169,7 @@ func (s *Scanner) scanIndent() (tok Token, lit string) {
_, _ = buf.WriteRune(ch)
}
}
switch buf.String() {
case "var":
return VAR, buf.String()

+ 60
- 136
circuitcompiler/parser.go

@ -2,9 +2,7 @@ package circuitcompiler
import (
"errors"
"fmt"
"io"
"os"
"regexp"
"strings"
)
@ -59,12 +57,18 @@ func (p *Parser) parseLine() (*Constraint, error) {
*/
c := &Constraint{}
tok, lit := p.scanIgnoreWhitespace()
c.Out = lit
c.Literal += lit
if c.Literal == "func" {
switch lit {
case "func":
c.Op = FUNC
// format: `func name(in):`
//todo this is all a bit hacky and unsafe
line, err := p.s.r.ReadString(':')
line = strings.Replace(line, " ", "", -1)
line = strings.Replace(line, ":", "", -1)
//set function name
//c.Literal = strings.Split(line, "(")[0]
c.Out = line
if err != nil {
return c, err
}
@ -72,59 +76,45 @@ func (p *Parser) parseLine() (*Constraint, error) {
rgx := regexp.MustCompile(`\((.*?)\)`)
insideParenthesis := rgx.FindStringSubmatch(line)
varsString := strings.Replace(insideParenthesis[1], " ", "", -1)
allInputs := strings.Split(varsString, ",")
// from allInputs, get the private and the public separated
for _, in := range allInputs {
if strings.Contains(in, "private") {
input := strings.Replace(in, "private", "", -1)
c.PrivateInputs = append(c.PrivateInputs, input)
} else if strings.Contains(in, "public") {
input := strings.Replace(in, "public", "", -1)
c.PublicInputs = append(c.PublicInputs, input)
} else {
// TODO give more info about the circuit code error
fmt.Println("error on declaration of public and private inputs")
os.Exit(0)
}
}
return c, nil
}
if c.Literal == "equals" {
// format: `equals(a, b)`
line, err := p.s.r.ReadString(')')
if err != nil {
return c, err
}
// read string inside ( )
rgx := regexp.MustCompile(`\((.*?)\)`)
insideParenthesis := rgx.FindStringSubmatch(line)
varsString := strings.Replace(insideParenthesis[1], " ", "", -1)
params := strings.Split(varsString, ",")
c.V1 = params[0]
c.V2 = params[1]
c.Inputs = strings.Split(varsString, ",")
return c, nil
case "var":
//var a = 234
//c.Literal += lit
_, lit = p.scanIgnoreWhitespace()
c.Out = lit
//c.Literal += lit
_, lit = p.scanIgnoreWhitespace() // skip =
//c.Literal += lit
// v1
_, lit = p.scanIgnoreWhitespace()
c.V1 = lit
//c.Literal += lit
break
case "#":
return nil, errors.New("comment parseline")
default:
c.Out = lit
//c.Literal += lit
_, lit = p.scanIgnoreWhitespace() // skip =
//c.Literal += lit
// v1
tok, lit = p.scanIgnoreWhitespace()
c.V1 = lit
//c.Literal += lit
// operator
tok, lit = p.scanIgnoreWhitespace()
c.Op = tok
//c.Literal += lit
// v2
_, lit = p.scanIgnoreWhitespace()
c.V2 = lit
//c.Literal += lit
}
// if c.Literal == "out" {
// // TODO
// return c, nil
// }
_, lit = p.scanIgnoreWhitespace() // skip =
c.Literal += lit
// v1
_, lit = p.scanIgnoreWhitespace()
c.V1 = lit
c.Literal += lit
// operator
_, lit = p.scanIgnoreWhitespace()
c.Op = lit
c.Literal += lit
// v2
_, lit = p.scanIgnoreWhitespace()
c.V2 = lit
c.Literal += lit
if tok == EOF {
return nil, errors.New("eof in parseline")
}
@ -151,92 +141,26 @@ func addToArrayIfNotExist(arr []string, elem string) []string {
}
// Parse parses the lines and returns the compiled Circuit
func (p *Parser) Parse() (*Circuit, error) {
circuit := &Circuit{}
circuit.Signals = append(circuit.Signals, "one")
nInputs := 0
func (p *Parser) Parse() (programm *Program, err error) {
programm = NewProgramm()
var circuit *Circuit
for {
constraint, err := p.parseLine()
if err != nil {
break
}
if constraint.Literal == "func" {
// one constraint for each input
for _, in := range constraint.PublicInputs {
newConstr := &Constraint{
Op: "in",
Out: in,
}
circuit.Constraints = append(circuit.Constraints, *newConstr)
nInputs++
circuit.Signals = addToArrayIfNotExist(circuit.Signals, in)
circuit.NPublic++
}
for _, in := range constraint.PrivateInputs {
newConstr := &Constraint{
Op: "in",
Out: in,
}
circuit.Constraints = append(circuit.Constraints, *newConstr)
nInputs++
circuit.Signals = addToArrayIfNotExist(circuit.Signals, in)
}
circuit.PublicInputs = constraint.PublicInputs
circuit.PrivateInputs = constraint.PrivateInputs
continue
if constraint.Op == FUNC {
circuit = programm.addFunction(constraint)
} else {
circuit.addConstraint(constraint)
}
if constraint.Literal == "equals" {
constr1 := &Constraint{
Op: "*",
V1: constraint.V2,
V2: "1",
Out: constraint.V1,
Literal: "equals(" + constraint.V1 + ", " + constraint.V2 + "): " + constraint.V1 + "==" + constraint.V2 + " * 1",
}
circuit.Constraints = append(circuit.Constraints, *constr1)
constr2 := &Constraint{
Op: "*",
V1: constraint.V1,
V2: "1",
Out: constraint.V2,
Literal: "equals(" + constraint.V1 + ", " + constraint.V2 + "): " + constraint.V2 + "==" + constraint.V1 + " * 1",
}
circuit.Constraints = append(circuit.Constraints, *constr2)
continue
}
circuit.Constraints = append(circuit.Constraints, *constraint)
isVal, _ := isValue(constraint.V1)
if !isVal {
circuit.Signals = addToArrayIfNotExist(circuit.Signals, constraint.V1)
}
isVal, _ = isValue(constraint.V2)
if !isVal {
circuit.Signals = addToArrayIfNotExist(circuit.Signals, constraint.V2)
}
// if constraint.Out == "out" {
// if Out is "out", put it after first value (one) and before the inputs
// if constraint.Out == circuit.PublicInputs[0] {
// if existInArray(circuit.PublicInputs, constraint.Out) {
// // if Out is a public signal, put it after first value (one) and before the private inputs
// if !existInArray(circuit.Signals, constraint.Out) {
// // if already don't exists in signal array
// signalsCopy := copyArray(circuit.Signals)
// var auxSignals []string
// auxSignals = append(auxSignals, signalsCopy[0])
// auxSignals = append(auxSignals, constraint.Out)
// auxSignals = append(auxSignals, signalsCopy[1:]...)
// circuit.Signals = auxSignals
// // circuit.PublicInputs = append(circuit.PublicInputs, constraint.Out)
// circuit.NPublic++
// }
// } else {
circuit.Signals = addToArrayIfNotExist(circuit.Signals, constraint.Out)
// }
}
circuit.NVars = len(circuit.Signals)
circuit.NSignals = len(circuit.Signals)
return circuit, nil
//TODO
//circuit.NVars = len(circuit.Signals)
//circuit.NSignals = len(circuit.Signals)
return programm, nil
}
func copyArray(in []string) []string { // tmp
var out []string

+ 3
- 3
cli/main.go

@ -11,9 +11,9 @@ import (
"math/big"
"os"
snark "github.com/arnaucube/go-snark"
"github.com/arnaucube/go-snark/circuitcompiler"
"github.com/arnaucube/go-snark/r1csqap"
snark "github.com/mottla/go-snark"
"github.com/mottla/go-snark/circuitcompiler"
"github.com/mottla/go-snark/r1csqap"
"github.com/urfave/cli"
)

+ 1
- 1
r1csqap/r1csqap.go

@ -4,7 +4,7 @@ import (
"bytes"
"math/big"
"github.com/arnaucube/go-snark/fields"
"github.com/mottla/go-snark/fields"
)
// Transpose transposes the *big.Int matrix

+ 1
- 1
r1csqap/r1csqap_test.go

@ -5,7 +5,7 @@ import (
"math/big"
"testing"
"github.com/arnaucube/go-snark/fields"
"github.com/mottla/go-snark/fields"
"github.com/stretchr/testify/assert"
)

+ 4
- 4
snark.go

@ -5,10 +5,10 @@ import (
"math/big"
"os"
"github.com/arnaucube/go-snark/bn128"
"github.com/arnaucube/go-snark/circuitcompiler"
"github.com/arnaucube/go-snark/fields"
"github.com/arnaucube/go-snark/r1csqap"
"github.com/mottla/go-snark/bn128"
"github.com/mottla/go-snark/circuitcompiler"
"github.com/mottla/go-snark/fields"
"github.com/mottla/go-snark/r1csqap"
)
// Setup is the data structure holding the Trusted Setup data. The Setup.Toxic sub struct must be destroyed after the GenerateTrustedSetup function is completed

+ 88
- 292
snark_test.go

@ -1,185 +1,77 @@
package snark
import (
"bytes"
"fmt"
"github.com/mottla/go-snark/circuitcompiler"
"github.com/mottla/go-snark/r1csqap"
"github.com/stretchr/testify/assert"
"math/big"
"strings"
"testing"
"time"
"github.com/arnaucube/go-snark/circuitcompiler"
"github.com/arnaucube/go-snark/r1csqap"
"github.com/stretchr/testify/assert"
)
func TestZkFromFlatCircuitCode(t *testing.T) {
// compile circuit and get the R1CS
// circuit function
// y = x^3 + x + 5
flatCode := `
func test(private s0, public s1):
s2 = s0 * s0
s3 = s2 * s0
s4 = s3 + s0
s5 = s4 + 5
equals(s1, s5)
out = 1 * 1
`
fmt.Print("\nflat code of the circuit:")
fmt.Println(flatCode)
// parse the code
parser := circuitcompiler.NewParser(strings.NewReader(flatCode))
circuit, err := parser.Parse()
assert.Nil(t, err)
// fmt.Println("\ncircuit data:", circuit)
// circuitJson, _ := json.Marshal(circuit)
// fmt.Println("circuit:", string(circuitJson))
b3 := big.NewInt(int64(3))
privateInputs := []*big.Int{b3}
b35 := big.NewInt(int64(35))
publicSignals := []*big.Int{b35}
// wittness
w, err := circuit.CalculateWitness(privateInputs, publicSignals)
assert.Nil(t, err)
// flat code to R1CS
fmt.Println("\ngenerating R1CS from flat code")
a, b, c := circuit.GenerateR1CS()
fmt.Println("\nR1CS:")
fmt.Println("a:", a)
fmt.Println("b:", b)
fmt.Println("c:", c)
// R1CS to QAP
// TODO zxQAP is not used and is an old impl, TODO remove
alphas, betas, gammas, zxQAP := Utils.PF.R1CSToQAP(a, b, c)
fmt.Println("qap")
assert.Equal(t, 8, len(alphas))
assert.Equal(t, 8, len(alphas))
assert.Equal(t, 8, len(alphas))
assert.Equal(t, 7, len(zxQAP))
assert.True(t, !bytes.Equal(alphas[1][1].Bytes(), big.NewInt(int64(0)).Bytes()))
ax, bx, cx, px := Utils.PF.CombinePolynomials(w, alphas, betas, gammas)
assert.Equal(t, 7, len(ax))
assert.Equal(t, 7, len(bx))
assert.Equal(t, 7, len(cx))
assert.Equal(t, 13, len(px))
hxQAP := Utils.PF.DivisorPolynomial(px, zxQAP)
assert.Equal(t, 7, len(hxQAP))
// hx==px/zx so px==hx*zx
assert.Equal(t, px, Utils.PF.Mul(hxQAP, zxQAP))
// p(x) = a(x) * b(x) - c(x) == h(x) * z(x)
abc := Utils.PF.Sub(Utils.PF.Mul(ax, bx), cx)
assert.Equal(t, abc, px)
hzQAP := Utils.PF.Mul(hxQAP, zxQAP)
assert.Equal(t, abc, hzQAP)
div, rem := Utils.PF.Div(px, zxQAP)
assert.Equal(t, hxQAP, div)
assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(6))
// calculate trusted setup
setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas)
assert.Nil(t, err)
fmt.Println("\nt:", setup.Toxic.T)
// zx and setup.Pk.Z should be the same (currently not, the correct one is the calculation used inside GenerateTrustedSetup function), the calculation is repeated. TODO avoid repeating calculation
assert.Equal(t, zxQAP, setup.Pk.Z)
hx := Utils.PF.DivisorPolynomial(px, setup.Pk.Z)
assert.Equal(t, hx, hxQAP)
// assert.Equal(t, hxQAP, hx)
div, rem = Utils.PF.Div(px, setup.Pk.Z)
assert.Equal(t, hx, div)
assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(6))
assert.Equal(t, px, Utils.PF.Mul(hxQAP, zxQAP))
// hx==px/zx so px==hx*zx
assert.Equal(t, px, Utils.PF.Mul(hx, setup.Pk.Z))
// check length of polynomials H(x) and Z(x)
assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1)
assert.Equal(t, len(hxQAP), len(px)-len(zxQAP)+1)
proof, err := GenerateProofs(*circuit, setup, w, px)
assert.Nil(t, err)
// fmt.Println("\n proofs:")
// fmt.Println(proof)
func TestNewProgramm(t *testing.T) {
// fmt.Println("public signals:", proof.PublicSignals)
fmt.Println("\nsignals:", circuit.Signals)
fmt.Println("witness:", w)
b35Verif := big.NewInt(int64(35))
publicSignalsVerif := []*big.Int{b35Verif}
before := time.Now()
assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true))
fmt.Println("verify proof time elapsed:", time.Since(before))
flat := `
// check that with another public input the verification returns false
bOtherWrongPublic := big.NewInt(int64(34))
wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic}
assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, true))
}
func add(x ,k):
z = k * x
out = x + mul(x,z)
func main(a,b):
out = add(a,b) * a
func TestZkMultiplication(t *testing.T) {
flatCode := `
func test(private a, private b, public c):
d = a * b
equals(c, d)
out = 1 * 1
func mul(a,b):
out = a * b
`
fmt.Println("flat code", flatCode)
// parse the code
parser := circuitcompiler.NewParser(strings.NewReader(flatCode))
circuit, err := parser.Parse()
assert.Nil(t, err)
b3 := big.NewInt(int64(3))
b4 := big.NewInt(int64(4))
privateInputs := []*big.Int{b3, b4}
b12 := big.NewInt(int64(12))
publicSignals := []*big.Int{b12}
// wittness
w, err := circuit.CalculateWitness(privateInputs, publicSignals)
assert.Nil(t, err)
// flat code to R1CS
fmt.Println("\ngenerating R1CS from flat code")
a, b, c := circuit.GenerateR1CS()
fmt.Println("\nR1CS:")
fmt.Println("a:", a)
fmt.Println("b:", b)
fmt.Println("c:", c)
parser := circuitcompiler.NewParser(strings.NewReader(flat))
program, err := parser.Parse()
if err != nil {
panic(err)
}
fmt.Println("\n unreduced")
fmt.Println(flat)
program.BuildConstraintTrees()
program.PrintConstraintTrees()
fmt.Println("\nReduced gates")
//PrintTree(froots["mul"])
gates := program.ReduceCombinedTree()
for _, g := range gates {
fmt.Println(g)
}
fmt.Println("generating R1CS")
a, b, c := program.GenerateReducedR1CS(gates)
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
a1 := big.NewInt(int64(6))
a2 := big.NewInt(int64(5))
inputs := []*big.Int{a1, a2}
w := program.CalculateWitness(inputs)
fmt.Println("witness")
fmt.Println(w)
// R1CS to QAP
// TODO zxQAP is not used and is an old impl. TODO remove
alphas, betas, gammas, zxQAP := Utils.PF.R1CSToQAP(a, b, c)
assert.Equal(t, 6, len(alphas))
assert.Equal(t, 6, len(betas))
assert.Equal(t, 6, len(betas))
assert.Equal(t, 5, len(zxQAP))
assert.True(t, !bytes.Equal(alphas[1][1].Bytes(), big.NewInt(int64(0)).Bytes()))
fmt.Println("qap")
fmt.Println("alphas", len(alphas))
fmt.Println("alphas", alphas)
fmt.Println("betas", len(betas))
fmt.Println("gammas", len(gammas))
fmt.Println("zx length", len(zxQAP))
ax, bx, cx, px := Utils.PF.CombinePolynomials(w, alphas, betas, gammas)
assert.Equal(t, 4, len(ax))
assert.Equal(t, 4, len(bx))
assert.Equal(t, 4, len(cx))
assert.Equal(t, 7, len(px))
fmt.Println("ax length", len(ax))
fmt.Println("bx length", len(bx))
fmt.Println("cx length", len(cx))
fmt.Println("px length", len(px))
hxQAP := Utils.PF.DivisorPolynomial(px, zxQAP)
assert.Equal(t, 3, len(hxQAP))
fmt.Println("hx length", len(hxQAP))
// hx==px/zx so px==hx*zx
assert.Equal(t, px, Utils.PF.Mul(hxQAP, zxQAP))
@ -195,135 +87,39 @@ func TestZkMultiplication(t *testing.T) {
assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4))
// calculate trusted setup
setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas)
assert.Nil(t, err)
// fmt.Println("\nt:", setup.Toxic.T)
// zx and setup.Pk.Z should be the same (currently not, the correct one is the calculation used inside GenerateTrustedSetup function), the calculation is repeated. TODO avoid repeating calculation
assert.Equal(t, zxQAP, setup.Pk.Z)
hx := Utils.PF.DivisorPolynomial(px, setup.Pk.Z)
assert.Equal(t, 3, len(hx))
assert.Equal(t, hx, hxQAP)
div, rem = Utils.PF.Div(px, setup.Pk.Z)
assert.Equal(t, hx, div)
assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4))
assert.Equal(t, px, Utils.PF.Mul(hxQAP, zxQAP))
// hx==px/zx so px==hx*zx
assert.Equal(t, px, Utils.PF.Mul(hx, setup.Pk.Z))
// check length of polynomials H(x) and Z(x)
assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1)
assert.Equal(t, len(hxQAP), len(px)-len(zxQAP)+1)
proof, err := GenerateProofs(*circuit, setup, w, px)
assert.Nil(t, err)
// fmt.Println("\n proofs:")
// fmt.Println(proof)
// fmt.Println("public signals:", proof.PublicSignals)
fmt.Println("\n", circuit.Signals)
fmt.Println("witness", w)
b12Verif := big.NewInt(int64(12))
publicSignalsVerif := []*big.Int{b12Verif}
before := time.Now()
assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true))
fmt.Println("verify proof time elapsed:", time.Since(before))
// check that with another public input the verification returns false
bOtherWrongPublic := big.NewInt(int64(11))
wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic}
assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, true))
}
func TestMinimalFlow(t *testing.T) {
// circuit function
// y = x^3 + x + 5
flatCode := `
func test(private s0, public s1):
s2 = s0 * s0
s3 = s2 * s0
s4 = s3 + s0
s5 = s4 + 5
equals(s1, s5)
out = 1 * 1
`
fmt.Print("\nflat code of the circuit:")
fmt.Println(flatCode)
// parse the code
parser := circuitcompiler.NewParser(strings.NewReader(flatCode))
circuit, err := parser.Parse()
assert.Nil(t, err)
b3 := big.NewInt(int64(3))
privateInputs := []*big.Int{b3}
b35 := big.NewInt(int64(35))
publicSignals := []*big.Int{b35}
// wittness
w, err := circuit.CalculateWitness(privateInputs, publicSignals)
assert.Nil(t, err)
// flat code to R1CS
fmt.Println("\ngenerating R1CS from flat code")
a, b, c := circuit.GenerateR1CS()
fmt.Println("\nR1CS:")
fmt.Println("a:", a)
fmt.Println("b:", b)
fmt.Println("c:", c)
// R1CS to QAP
// TODO zxQAP is not used and is an old impl, TODO remove
alphas, betas, gammas, _ := Utils.PF.R1CSToQAP(a, b, c)
fmt.Println("qap")
assert.Equal(t, 8, len(alphas))
assert.Equal(t, 8, len(alphas))
assert.Equal(t, 8, len(alphas))
assert.True(t, !bytes.Equal(alphas[1][1].Bytes(), big.NewInt(int64(0)).Bytes()))
ax, bx, cx, px := Utils.PF.CombinePolynomials(w, alphas, betas, gammas)
assert.Equal(t, 7, len(ax))
assert.Equal(t, 7, len(bx))
assert.Equal(t, 7, len(cx))
assert.Equal(t, 13, len(px))
// calculate trusted setup
setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas)
assert.Nil(t, err)
fmt.Println("\nt:", setup.Toxic.T)
hx := Utils.PF.DivisorPolynomial(px, setup.Pk.Z)
div, rem := Utils.PF.Div(px, setup.Pk.Z)
assert.Equal(t, hx, div)
assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(6))
// hx==px/zx so px==hx*zx
assert.Equal(t, px, Utils.PF.Mul(hx, setup.Pk.Z))
// check length of polynomials H(x) and Z(x)
assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1)
proof, err := GenerateProofs(*circuit, setup, w, px)
assert.Nil(t, err)
// fmt.Println("\n proofs:")
// fmt.Println(proof)
// fmt.Println("public signals:", proof.PublicSignals)
fmt.Println("\nsignals:", circuit.Signals)
fmt.Println("witness:", w)
b35Verif := big.NewInt(int64(35))
publicSignalsVerif := []*big.Int{b35Verif}
before := time.Now()
assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true))
fmt.Println("verify proof time elapsed:", time.Since(before))
// check that with another public input the verification returns false
bOtherWrongPublic := big.NewInt(int64(34))
wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic}
assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, true))
//setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas)
//assert.Nil(t, err)
//fmt.Println("\nt:", setup.Toxic.T)
//
//// zx and setup.Pk.Z should be the same (currently not, the correct one is the calculation used inside GenerateTrustedSetup function), the calculation is repeated. TODO avoid repeating calculation
//// assert.Equal(t, zxQAP, setup.Pk.Z)
//
//fmt.Println("hx pk.z", hxQAP)
//hx := Utils.PF.DivisorPolynomial(px, setup.Pk.Z)
//fmt.Println("hx pk.z", hx)
//// assert.Equal(t, hxQAP, hx)
//assert.Equal(t, px, Utils.PF.Mul(hxQAP, zxQAP))
//assert.Equal(t, px, Utils.PF.Mul(hx, setup.Pk.Z))
//
//assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1)
//assert.Equal(t, len(hxQAP), len(px)-len(zxQAP)+1)
//// fmt.Println("pk.Z", len(setup.Pk.Z))
//// fmt.Println("zxQAP", len(zxQAP))
//
//// piA = g1 * A(t), piB = g2 * B(t), piC = g1 * C(t), piH = g1 * H(t)
//proof, err := GenerateProofs(*circuit, setup, w, px)
//assert.Nil(t, err)
//
//// fmt.Println("\n proofs:")
//// fmt.Println(proof)
//
//// fmt.Println("public signals:", proof.PublicSignals)
//fmt.Println("\nwitness", w)
//// b1 := big.NewInt(int64(1))
//b35 := big.NewInt(int64(35))
//// publicSignals := []*big.Int{b1, b35}
//publicSignals := []*big.Int{b35}
//before := time.Now()
//assert.True(t, VerifyProof(*circuit, setup, proof, publicSignals, true))
//fmt.Println("verify proof time elapsed:", time.Since(before))
}

Loading…
Cancel
Save