mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 11:16:42 +01:00
Compare commits
1 Commits
feature/se
...
wasm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fc1bf5e97 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -61,9 +61,3 @@ typings/
|
||||
.next
|
||||
|
||||
tmp
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Workspace files are user-specific
|
||||
*.sublime-workspace
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".",
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"SublimeAnarchyDebug": {
|
||||
"debug": {
|
||||
"executable": "${project_path}/test/circuits/add",
|
||||
"params": [
|
||||
"addin.json",
|
||||
"out.bin",
|
||||
],
|
||||
"path": [
|
||||
],
|
||||
"environment": [
|
||||
],
|
||||
"working_dir": "${project_path}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
README.md
27
README.md
@@ -1,18 +1,12 @@
|
||||
# Circom
|
||||
# Circon
|
||||
|
||||
Circom is a language designed to write arithmetic circuits that can be used in zero knowledge proofs.
|
||||
Circon is a language designed to write arithmetic circuits that can be used in zero knowledge proofs.
|
||||
|
||||
In particular, it is designed to work in [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Tutorial
|
||||
|
||||
A good starting point [is this tutorial](https://github.com/iden3/circom/blob/master/TUTORIAL.md)
|
||||
|
||||
Also this [video](https://www.youtube.com/watch?v=-9TJa1hVsKA) is a good starting point.
|
||||
|
||||
### First circuit
|
||||
|
||||
Creation of a circuit. This is an example of a NAND door:
|
||||
@@ -31,7 +25,7 @@ template NAND() {
|
||||
component main = NAND();
|
||||
```
|
||||
|
||||
The language uses mainly JavaScript/C syntax together with 5 extra operators to define the following constraints:
|
||||
The language uses mainly JavaScript/C syntax together with 5 extra operators to define the constraints:
|
||||
|
||||
`<==` , `==>` : These two operators are used to connect signals and at the same time imply a constraint.
|
||||
|
||||
@@ -45,7 +39,7 @@ In the above example, both inputs are forced to be binary by adding the constrai
|
||||
|
||||
### Compilation the circuit
|
||||
|
||||
First of all, the compiler must be installed by typing:
|
||||
First of all, the compiler must be installed typing:
|
||||
|
||||
```
|
||||
npm install -g circom
|
||||
@@ -54,7 +48,7 @@ npm install -g circom
|
||||
The circuit is compiled with the following command:
|
||||
|
||||
```
|
||||
circom mycircuit.circom -o mycircuit.json
|
||||
circom -s mycircuit.circom -o mycircuit.json
|
||||
```
|
||||
|
||||
The resulting output ( `mycircuit.json` ) can be used in the [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
||||
@@ -97,7 +91,7 @@ in === out[0]*2**0 + out[1]*2**1 + out[2]*2**2 + ... + out[n-1]*2**(n-1)
|
||||
```
|
||||
|
||||
We do this by using a variable `lc1` and adding each signal multiplied by its coefficient.
|
||||
This variable does not hold a value at compilation time, but it holds a linear combination and it is used in the last constraint:
|
||||
This variable does not hold a value in compilation time, but it holds a linear combination and it is used in the last constraint:
|
||||
|
||||
```
|
||||
lc1 === in;
|
||||
@@ -177,7 +171,7 @@ To waranty binary outputs:
|
||||
.
|
||||
.
|
||||
.
|
||||
out[n+e-1] * (out[n+e-1] - 1) === 0
|
||||
out[n+e-1] * (out[n+e-1] - 1) == 0
|
||||
|
||||
*/
|
||||
|
||||
@@ -260,14 +254,9 @@ component main = Adder();
|
||||
In this example we have shown how to design a top-down circuit with many subcircuits and how to connect them together. One can also see that auxiliary functions to do specific computations can be created.
|
||||
|
||||
|
||||
### More examples.
|
||||
|
||||
You can find more examples in this library of basic circits [circomlib](https://github.com/iden3/circomlib)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Circom is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details.
|
||||
Circon is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
254
TUTORIAL.md
254
TUTORIAL.md
@@ -1,254 +0,0 @@
|
||||
# circom and snarkjs tutorial
|
||||
|
||||
This tutorial will guide you in creating your first Zero Knowledge zkSnark circuit. It will take you through the various techniques to write circuits, and will show you how to create proofs and verify them off-chain and on-chain on Ethereum.
|
||||
|
||||
## 1. Installing the tools
|
||||
|
||||
### 1.1 Pre-requisites
|
||||
|
||||
If you don't have it installed yet, you need to install `Node.js`.
|
||||
|
||||
The last stable version of `Node.js` (or 8.12.0) works just fine, but if you install the latest current version `Node.js` (10.12.0) you will see a significant increase in performance. This is because last versions of node includes Big Integer Libraries nativelly. The `snarkjs` library makes use of this feature if available, and this improves the performance x10 (!).
|
||||
|
||||
### 1.2 Install **circom** and **snarkjs**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
npm install -g circom
|
||||
npm install -g snarkjs
|
||||
```
|
||||
|
||||
|
||||
## 2. Working with a circuit
|
||||
|
||||
Let's create a circuit that tries to prove that you are able to factor a number!
|
||||
|
||||
### 2.1 Create a circuit in a new directory
|
||||
|
||||
1. Create an empty directory called `factor` where you will put all the files that you will use in this tutorial.
|
||||
```
|
||||
mkdir factor
|
||||
cd factor
|
||||
```
|
||||
|
||||
|
||||
> In a real circuit, you will probably want to create a `git` repository with a `circuits` directory and a `test` directory with all your tests, and the needed scripts to build all the circuits.
|
||||
|
||||
2. Create a new file named `circuit.circom` with the following content:
|
||||
|
||||
```
|
||||
template Multiplier() {
|
||||
signal private input a;
|
||||
signal private input b;
|
||||
signal output c;
|
||||
|
||||
c <== a*b;
|
||||
}
|
||||
|
||||
component main = Multiplier();
|
||||
```
|
||||
|
||||
This circuit has 2 private input signals named `a` and `b` and one output named `c`.
|
||||
|
||||
The only thing that the circuit does is forcing the signal `c` to be the value of `a*b`
|
||||
|
||||
After declaring the `Multiplier` template, we instantiate it with a component named`main`.
|
||||
|
||||
Note: When compiling a circuit, a component named `main` must always exist.
|
||||
|
||||
### 2.2 Compile the circuit
|
||||
|
||||
We are now ready to compile the circuit. Run the following command:
|
||||
|
||||
```sh
|
||||
circom circuit.circom -o circuit.json
|
||||
```
|
||||
|
||||
to compile the circuit to a file named `circuit.json`
|
||||
|
||||
|
||||
## 3. Taking the compiled circuit to *snarkjs*
|
||||
|
||||
Now that the circuit is compiled, we will continue with `snarkjs`.
|
||||
Please note that you can always access the help of `snarkjs` by typing:
|
||||
|
||||
```sh
|
||||
snarkjs --help
|
||||
```
|
||||
|
||||
### 3.1 View information and stats regarding a circuit
|
||||
|
||||
To show general statistics of this circuit, you can run:
|
||||
|
||||
```sh
|
||||
snarkjs info -c circuit.json
|
||||
```
|
||||
|
||||
You can also print the constraints of the circuit by running:
|
||||
|
||||
```sh
|
||||
snarkjs printconstraints -c circuit.json
|
||||
```
|
||||
|
||||
|
||||
### 3.2 Setting up using *snarkjs*
|
||||
|
||||
|
||||
Ok, let's run a setup for our circuit:
|
||||
|
||||
```sh
|
||||
snarkjs setup
|
||||
```
|
||||
|
||||
> By default `snarkjs` will look for and use `circuit.json`. You can always specify a different circuit file by adding `-c <circuit JSON file name>`
|
||||
|
||||
The output of the setup will in the form of 2 files: `proving_key.json` and `verification_key.json`
|
||||
|
||||
### 3.3. Calculating a witness
|
||||
|
||||
Before creating any proof, we need to calculate all the signals of the circuit that match (all) the constrains of the circuit.
|
||||
|
||||
`snarkjs` calculates those for you. You need to provide a file with the inputs and it will execute the circuit and calculate all the intermediate signals and the output. This set of signals is the *witness*.
|
||||
|
||||
The zero knowledge proofs prove that you know a set of signals (witness) that match all the constraints, without revealing any of the signals except the public inputs plus the outputs.
|
||||
|
||||
For example, imagine you want to prove you are able to factor 33. It means that you know two numbers `a` and `b` and when you multiply them, it results in 33.
|
||||
|
||||
> Of course you can always use one and the same number as `a` and `b`. We will deal with this problem later.
|
||||
|
||||
So you want to prove that you know 3 and 11.
|
||||
|
||||
Let's create a file named `input.json`
|
||||
|
||||
```json
|
||||
{"a": 3, "b": 11}
|
||||
```
|
||||
|
||||
Now let's calculate the witness:
|
||||
|
||||
```sh
|
||||
snarkjs calculatewitness
|
||||
```
|
||||
|
||||
You may want to take a look at `witness.json` file with all the signals.
|
||||
|
||||
### Create the proof
|
||||
|
||||
Now that we have the witness generated, we can create the proof.
|
||||
|
||||
```sh
|
||||
snarkjs proof
|
||||
```
|
||||
|
||||
This command will use the `proving_key.json` and the `witness.json` files by default to generate `proof.json` and `public.json`
|
||||
|
||||
The `proof.json` file will contain the actual proof. And the `public.json` file will contain just the values of the public inputs and the outputs.
|
||||
|
||||
|
||||
### Verifying the proof
|
||||
|
||||
To verify the proof run:
|
||||
|
||||
```sh
|
||||
snarkjs verify
|
||||
```
|
||||
|
||||
This command will use `verification_key.json`, `proof.json` and `public.json` to verify that is valid.
|
||||
|
||||
Here we are verifying that we know a witness that the public inputs and the outputs matches the ones in the `public.json` file.
|
||||
|
||||
|
||||
If the proof is ok, you will see `OK` or `INVALID` if not ok.
|
||||
|
||||
### Generate the solidity verifier
|
||||
|
||||
```sh
|
||||
snarkjs generateverifier
|
||||
```
|
||||
|
||||
This command will take the `verification_key.json` and generate solidity code in `verifier.sol` file.
|
||||
|
||||
You can take the code in `verifier.sol` and cut and paste it in remix.
|
||||
|
||||
This code contains two contracts: Pairings and Verifier. You only need to deploy the `Verifier` contract.
|
||||
|
||||
> You may want to use a test net like Rinkeby, Kovan or Ropsten. You can also use the Javascript VM, but in some browsers, the verification takes long and it may hang the page.
|
||||
|
||||
|
||||
### Verifying the proof on-chain
|
||||
|
||||
The verifier contract deployed in the last step has a `view` function called `verifyProof`.
|
||||
|
||||
This function will return true if the proof and the inputs are valid.
|
||||
|
||||
To facilitate the call, you can use snarkjs to generate the parameters of the call by typing:
|
||||
|
||||
```sh
|
||||
snarkjs generatecall
|
||||
```
|
||||
|
||||
Just cut and paste the output to the parameters field of the `verifyProof` method in Remix.
|
||||
|
||||
If every thing works ok, this method should return true.
|
||||
|
||||
If you change any bit in the parameters, the result will be verifiably false.
|
||||
|
||||
|
||||
## Bonus track
|
||||
|
||||
We can fix the circuit to not accept one as any of the values by adding some extra constraints.
|
||||
|
||||
Here the trick is that we use the property that 0 has no inverse. So `(a-1)` should not have an inverse.
|
||||
|
||||
That means that `(a-1)*inv = 1` will be inpossible to match if `a` is 1.
|
||||
|
||||
We just calculate inv by `1/(a-1)`
|
||||
|
||||
So let's modify the circuit:
|
||||
|
||||
```
|
||||
template Multiplier() {
|
||||
signal private input a;
|
||||
signal private input b;
|
||||
signal output c;
|
||||
signal inva;
|
||||
signal invb;
|
||||
|
||||
inva <-- 1/(a-1);
|
||||
(a-1)*inva === 1;
|
||||
|
||||
invb <-- 1/(b-1);
|
||||
(b-1)*invb === 1;
|
||||
|
||||
c <== a*b;
|
||||
}
|
||||
|
||||
component main = Multiplier();
|
||||
```
|
||||
|
||||
A nice thing of the circom language is that you can split a <== into two independent actions: <-- and ===
|
||||
|
||||
The <-- and --> operators assign a value to a signal without creating any constraints.
|
||||
|
||||
The === operator adds a constraint without assigning any value to any signal.
|
||||
|
||||
The circuit also has another problem: the operation works in Zr, so we need to guarantee the multiplication does not overflow. This can be done by converting the inputs to binary and checking the ranges, but we will reserve it for future tutorials.
|
||||
|
||||
## Where to go from here:
|
||||
|
||||
You may want to read the [README](https://github.com/iden3/circom) to learn more features about circom.
|
||||
|
||||
You can also check a library with many basic circuits lib binarizations, comparators, eddsa, hashes, merkle trees etc [here](https://github.com/iden3/circomlib) (Work in progress).
|
||||
|
||||
|
||||
Or a exponentiation in the Baby Jub curve [here](https://github.com/iden3/circomlib) (Work in progress).
|
||||
|
||||
|
||||
# Final note
|
||||
|
||||
There is nothing worse for a dev than working with a buggy compiler. This is a very early stage of the compiler, so there are many bugs and lots of work needs to be done. Please have it present if you are doing anything serious with it.
|
||||
|
||||
And please contact us for any isue you have. In general, a github issue with a small piece of code with the bug is very useful to us.
|
||||
|
||||
Enjoy zero knowledge proving!
|
||||
@@ -1,245 +0,0 @@
|
||||
<% function addS1S2() { %>
|
||||
xor rdx, rdx
|
||||
mov edx, eax
|
||||
add edx, ecx
|
||||
jo add_manageOverflow ; rsi already is the 64bits result
|
||||
|
||||
mov [rdi], rdx ; not necessary to adjust so just save and return
|
||||
ret
|
||||
|
||||
add_manageOverflow: ; Do the operation in 64 bits
|
||||
push rsi
|
||||
movsx rsi, eax
|
||||
movsx rdx, ecx
|
||||
add rsi, rdx
|
||||
call rawCopyS2L
|
||||
pop rsi
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
|
||||
|
||||
<% function addL1S2() { %>
|
||||
add rsi, 8
|
||||
movsx rdx, ecx
|
||||
add rdi, 8
|
||||
cmp rdx, 0
|
||||
<% const rawAddLabel = global.tmpLabel() %>
|
||||
jns <%= rawAddLabel %>
|
||||
neg rdx
|
||||
call rawSubLS
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<%= rawAddLabel %>:
|
||||
call rawAddLS
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
|
||||
<% } %>
|
||||
|
||||
<% function addS1L2() { %>
|
||||
lea rsi, [rdx + 8]
|
||||
movsx rdx, eax
|
||||
add rdi, 8
|
||||
cmp rdx, 0
|
||||
<% const rawAddLabel = global.tmpLabel() %>
|
||||
jns <%= rawAddLabel %>
|
||||
neg rdx
|
||||
call rawSubLS
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<%= rawAddLabel %>:
|
||||
call rawAddLS
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
<% function addL1L2() { %>
|
||||
add rdi, 8
|
||||
add rsi, 8
|
||||
add rdx, 8
|
||||
call rawAddLL
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; add
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdx <= Pointer to element 2
|
||||
; rdi <= Pointer to result
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_add:
|
||||
mov rax, [rsi]
|
||||
mov rcx, [rdx]
|
||||
bt rax, 63 ; Check if is short first operand
|
||||
jc add_l1
|
||||
bt rcx, 63 ; Check if is short second operand
|
||||
jc add_s1l2
|
||||
|
||||
add_s1s2: ; Both operands are short
|
||||
<%= addS1S2() %>
|
||||
add_l1:
|
||||
bt rcx, 63 ; Check if is short second operand
|
||||
jc add_l1l2
|
||||
|
||||
;;;;;;;;
|
||||
add_l1s2:
|
||||
bt rax, 62 ; check if montgomery first
|
||||
jc add_l1ms2
|
||||
add_l1ns2:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= addL1S2(); %>
|
||||
|
||||
add_l1ms2:
|
||||
bt rcx, 62 ; check if montgomery second
|
||||
jc add_l1ms2m
|
||||
add_l1ms2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_b() %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
add_l1ms2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
|
||||
;;;;;;;;
|
||||
add_s1l2:
|
||||
bt rcx, 62 ; check if montgomery first
|
||||
jc add_s1l2m
|
||||
add_s1l2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= addS1L2(); %>
|
||||
|
||||
add_s1l2m:
|
||||
bt rax, 62 ; check if montgomery second
|
||||
jc add_s1ml2m
|
||||
add_s1nl2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_a() %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
add_s1ml2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
;;;;
|
||||
add_l1l2:
|
||||
bt rax, 62 ; check if montgomery first
|
||||
jc add_l1ml2
|
||||
add_l1nl2:
|
||||
bt rcx, 62 ; check if montgomery second
|
||||
jc add_l1nl2m
|
||||
add_l1nl2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
add_l1nl2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_a(); %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
add_l1ml2:
|
||||
bt rcx, 62 ; check if montgomery seconf
|
||||
jc add_l1ml2m
|
||||
add_l1ml2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_b(); %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
add_l1ml2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= addL1L2() %>
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawAddLL
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of type long
|
||||
; Params:
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
; rdx <= Pointer to the long data of element 2
|
||||
; rdi <= Pointer to the long data of result
|
||||
; Modified Registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawAddLL:
|
||||
; Add component by component with carry
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rsi + <%=i*8%>]
|
||||
<%= i==0 ? "add" : "adc" %> rax, [rdx + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
jc rawAddLL_sq ; if overflow, substract q
|
||||
|
||||
; Compare with q
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
<% if (i>0) { %>
|
||||
mov rax, [rdi + <%= (n64-i-1)*8 %>]
|
||||
<% } %>
|
||||
cmp rax, [q + <%= (n64-i-1)*8 %>]
|
||||
jc rawAddLL_done ; q is bigget so done.
|
||||
jnz rawAddLL_sq ; q is lower
|
||||
<% } %>
|
||||
; If equal substract q
|
||||
rawAddLL_sq:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "sub" : "sbb" %> [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
rawAddLL_done:
|
||||
ret
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawAddLS
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of type long
|
||||
; Params:
|
||||
; rdi <= Pointer to the long data of result
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
; rdx <= Value to be added
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawAddLS:
|
||||
; Add component by component with carry
|
||||
|
||||
add rdx, [rsi]
|
||||
mov [rdi] ,rdx
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rdx, 0
|
||||
adc rdx, [rsi + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rdx
|
||||
<% } %>
|
||||
jc rawAddLS_sq ; if overflow, substract q
|
||||
|
||||
; Compare with q
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rdi + <%= (n64-i-1)*8 %>]
|
||||
cmp rax, [q + <%= (n64-i-1)*8 %>]
|
||||
jc rawAddLS_done ; q is bigget so done.
|
||||
jnz rawAddLS_sq ; q is lower
|
||||
<% } %>
|
||||
; If equal substract q
|
||||
rawAddLS_sq:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "sub" : "sbb" %> [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
rawAddLS_done:
|
||||
ret
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
<% function binOpS1S2(op) { %>
|
||||
cmp r8d, 0
|
||||
<% const s1s2_solveNeg = global.tmpLabel() %>
|
||||
js <%=s1s2_solveNeg%>
|
||||
|
||||
cmp r9d, 0
|
||||
js <%=s1s2_solveNeg%>
|
||||
xor rdx, rdx ; both ops are positive so do the op and return
|
||||
mov edx, r8d
|
||||
<%=op%> edx, r9d
|
||||
mov [rdi], rdx ; not necessary to adjust so just save and return
|
||||
ret
|
||||
|
||||
<%=s1s2_solveNeg%>:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= global.toLong_b() %>
|
||||
<%= global.toLong_a() %>
|
||||
<%= binOpL1L2(op) %>
|
||||
|
||||
|
||||
<% } %>
|
||||
|
||||
<% function binOpS1L2(op) { %>
|
||||
cmp r8d, 0
|
||||
<% const s1l2_solveNeg = global.tmpLabel() %>
|
||||
js <%=s1l2_solveNeg%>
|
||||
movsx rax, r8d
|
||||
<%=op%> rax, [rdx +8]
|
||||
mov [rdi+8], rax
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
xor rax, rax
|
||||
<%=op%> rax, [rdx + <%= (i*8)+8 %>]
|
||||
<% if (i== n64-1) { %>
|
||||
and rax, [lboMask]
|
||||
<% } %>
|
||||
mov [rdi + <%= (i*8)+8 %> ], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
<%=s1l2_solveNeg%>:
|
||||
<%= global.toLong_a() %>
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= binOpL1L2(op) %>
|
||||
|
||||
<% } %>
|
||||
|
||||
<% function binOpL1S2(op) { %>
|
||||
cmp r9d, 0
|
||||
<% const l1s2_solveNeg = global.tmpLabel() %>
|
||||
js <%=l1s2_solveNeg%>
|
||||
movsx rax, r9d
|
||||
<%=op%> rax, [rsi +8]
|
||||
mov [rdi+8], rax
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
xor rax, rax
|
||||
<%=op%> rax, [rsi + <%= (i*8)+8 %>];
|
||||
<% if (i== n64-1) { %>
|
||||
and rax, [lboMask] ;
|
||||
<% } %>
|
||||
mov [rdi + <%= (i*8)+8 %> ], rax;
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
<%=l1s2_solveNeg%>:
|
||||
<%= global.toLong_b() %>
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= binOpL1L2(op) %>
|
||||
|
||||
<% } %>
|
||||
|
||||
<% function binOpL1L2(op) { %>
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rsi + <%= (i*8)+8 %>]
|
||||
<%=op%> rax, [rdx + <%= (i*8)+8 %>]
|
||||
<% if (i== n64-1) { %>
|
||||
and rax, [lboMask]
|
||||
<% } %>
|
||||
mov [rdi + <%= (i*8)+8 %> ], rax
|
||||
<% } %>
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
|
||||
|
||||
|
||||
<% function binOp(op) { %>
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; b<%= op %>
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdx <= Pointer to element 2
|
||||
; rdi <= Pointer to result
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_b<%=op%>:
|
||||
mov r8, [rsi]
|
||||
mov r9, [rdx]
|
||||
bt r8, 63 ; Check if is short first operand
|
||||
jc <%=op%>_l1
|
||||
bt r9, 63 ; Check if is short second operand
|
||||
jc <%=op%>_s1l2
|
||||
|
||||
<%=op%>_s1s2:
|
||||
<%= binOpS1S2(op) %>
|
||||
|
||||
|
||||
<%=op%>_l1:
|
||||
bt r9, 63 ; Check if is short second operand
|
||||
jc <%=op%>_l1l2
|
||||
|
||||
|
||||
<%=op%>_l1s2:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jc <%=op%>_l1ms2
|
||||
<%=op%>_l1ns2:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= binOpL1S2(op) %>
|
||||
|
||||
<%=op%>_l1ms2:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
push r9 ; r9 is used in montgomery so we need to save it
|
||||
<%= global.fromMont_a() %>
|
||||
pop r9
|
||||
<%= binOpL1S2(op) %>
|
||||
|
||||
|
||||
<%=op%>_s1l2:
|
||||
bt r9, 62 ; check if montgomery first
|
||||
jc <%=op%>_s1l2m
|
||||
<%=op%>_s1l2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= binOpS1L2(op) %>
|
||||
|
||||
<%=op%>_s1l2m:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
push r8 ; r8 is used in montgomery so we need to save it
|
||||
<%= global.fromMont_b() %>
|
||||
pop r8
|
||||
<%= binOpS1L2(op) %>
|
||||
|
||||
|
||||
<%=op%>_l1l2:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jc <%=op%>_l1ml2
|
||||
bt r9, 62 ; check if montgomery first
|
||||
jc <%=op%>_l1nl2m
|
||||
<%=op%>_l1nl2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= binOpL1L2(op) %>
|
||||
|
||||
<%=op%>_l1nl2m:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= global.fromMont_b() %>
|
||||
<%= binOpL1L2(op) %>
|
||||
|
||||
<%=op%>_l1ml2:
|
||||
bt r9, 62 ; check if montgomery first
|
||||
jc <%=op%>_l1ml2m
|
||||
<%=op%>_l1ml2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= global.fromMont_a() %>
|
||||
<%= binOpL1L2(op) %>
|
||||
|
||||
<%=op%>_l1ml2m:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= global.fromMont_a() %>
|
||||
<%= global.fromMont_b() %>
|
||||
<%= binOpL1L2(op) %>
|
||||
<% } %>
|
||||
|
||||
<%= binOp("and") %>
|
||||
<%= binOp("or") %>
|
||||
<%= binOp("xor") %>
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; bnot
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdi <= Pointer to result
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_bnot:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
|
||||
mov r8, [rsi]
|
||||
bt r8, 63 ; Check if is long operand
|
||||
jc bnot_l1
|
||||
bnot_s:
|
||||
<%= global.toLong_a() %>
|
||||
jmp bnot_l1n
|
||||
|
||||
bnot_l1:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jnc bnot_l1n
|
||||
|
||||
bnot_l1m:
|
||||
<%= global.fromMont_a() %>
|
||||
|
||||
bnot_l1n:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rsi + <%= i*8 + 8 %>]
|
||||
not rax
|
||||
<% if (i== n64-1) { %>
|
||||
and rax, [lboMask]
|
||||
<% } %>
|
||||
mov [rdi + <%= i*8 + 8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
const bigInt=require("big-integer");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const renderFile = util.promisify(require("ejs").renderFile);
|
||||
|
||||
const runningAsScript = !module.parent;
|
||||
|
||||
|
||||
class ZqBuilder {
|
||||
constructor(q, name) {
|
||||
const self = this;
|
||||
this.q=bigInt(q);
|
||||
this.n64 = Math.floor((this.q.bitLength() - 1) / 64)+1;
|
||||
this.name = name;
|
||||
this.bigInt = bigInt;
|
||||
this.lastTmp=0;
|
||||
this.global = {};
|
||||
this.global.tmpLabel = function(label) {
|
||||
self.lastTmp++;
|
||||
label = label || "tmp";
|
||||
return label+"_"+self.lastTmp;
|
||||
};
|
||||
}
|
||||
|
||||
constantElement(v) {
|
||||
let S = "";
|
||||
const mask = bigInt("FFFFFFFFFFFFFFFF", 16);
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
if (i>0) S = S+",";
|
||||
let shex = v.shiftRight(i*64).and(mask).toString(16);
|
||||
while (shex.length <16) shex = "0" + shex;
|
||||
S = S + "0x" + shex;
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function buildField(q, name) {
|
||||
const builder = new ZqBuilder(q, name);
|
||||
|
||||
const asm = await renderFile(path.join(__dirname, "fr.asm.ejs"), builder);
|
||||
const c = await renderFile(path.join(__dirname, "fr.c.ejs"), builder);
|
||||
const h = await renderFile(path.join(__dirname, "fr.h.ejs"), builder);
|
||||
|
||||
return {asm: asm, h: h, c: c};
|
||||
}
|
||||
|
||||
if (runningAsScript) {
|
||||
const fs = require("fs");
|
||||
var argv = require("yargs")
|
||||
.usage("Usage: $0 -q [primeNum] -n [name] -oc [out .c file] -oh [out .h file]")
|
||||
.demandOption(["q","n"])
|
||||
.alias("q", "prime")
|
||||
.alias("n", "name")
|
||||
.argv;
|
||||
|
||||
const q = bigInt(argv.q);
|
||||
|
||||
const asmFileName = (argv.oc) ? argv.oc : argv.name.toLowerCase() + ".asm";
|
||||
const hFileName = (argv.oc) ? argv.oc : argv.name.toLowerCase() + ".h";
|
||||
const cFileName = (argv.oc) ? argv.oc : argv.name.toLowerCase() + ".c";
|
||||
|
||||
buildField(q, argv.name).then( (res) => {
|
||||
fs.writeFileSync(asmFileName, res.asm, "utf8");
|
||||
fs.writeFileSync(hFileName, res.h, "utf8");
|
||||
fs.writeFileSync(cFileName, res.c, "utf8");
|
||||
});
|
||||
|
||||
} else {
|
||||
module.exports = buildField;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
const chai = require("chai");
|
||||
const assert = chai.assert;
|
||||
|
||||
const fs = require("fs");
|
||||
var tmp = require("tmp-promise");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
|
||||
const BuildZqField = require("./buildzqfield");
|
||||
|
||||
module.exports = testField;
|
||||
|
||||
async function testField(prime, test) {
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
|
||||
|
||||
const source = await BuildZqField(prime, "Fr");
|
||||
|
||||
// console.log(dir.path);
|
||||
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.asm"), source.asm, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.h"), source.h, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.c"), source.c, "utf8");
|
||||
|
||||
await exec(`cp ${path.join(__dirname, "tester.cpp")} ${dir.path}`);
|
||||
|
||||
await exec("nasm -fmacho64 --prefix _ " +
|
||||
` ${path.join(dir.path, "fr.asm")}`
|
||||
);
|
||||
|
||||
await exec("g++" +
|
||||
` ${path.join(dir.path, "tester.cpp")}` +
|
||||
` ${path.join(dir.path, "fr.o")}` +
|
||||
` ${path.join(dir.path, "fr.c")}` +
|
||||
` -o ${path.join(dir.path, "tester")}` +
|
||||
" -lgmp -g"
|
||||
);
|
||||
|
||||
const inLines = [];
|
||||
for (let i=0; i<test.length; i++) {
|
||||
for (let j=0; j<test[i][0].length; j++) {
|
||||
inLines.push(test[i][0][j]);
|
||||
}
|
||||
}
|
||||
inLines.push("");
|
||||
|
||||
await fs.promises.writeFile(path.join(dir.path, "in.tst"), inLines.join("\n"), "utf8");
|
||||
|
||||
await exec(`${path.join(dir.path, "tester")}` +
|
||||
` <${path.join(dir.path, "in.tst")}` +
|
||||
` >${path.join(dir.path, "out.tst")}`);
|
||||
|
||||
const res = await fs.promises.readFile(path.join(dir.path, "out.tst"), "utf8");
|
||||
const resLines = res.split("\n");
|
||||
|
||||
for (let i=0; i<test.length; i++) {
|
||||
const expected = test[i][1].toString();
|
||||
const calculated = resLines[i];
|
||||
|
||||
if (calculated != expected) {
|
||||
console.log("FAILED");
|
||||
for (let j=0; j<test[i][0].length; j++) {
|
||||
console.log(test[i][0][j]);
|
||||
}
|
||||
console.log("Should Return: " + expected);
|
||||
console.log("But Returns: " + calculated);
|
||||
}
|
||||
|
||||
assert.equal(calculated, expected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
|
||||
<% function retOne() { %>
|
||||
mov qword [rdi], 1
|
||||
add rsp, <%= (n64+1)*8 %>
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
<% function retZero() { %>
|
||||
mov qword [rdi], 0
|
||||
add rsp, <%= (n64+1)*8 %>
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
<% function cmpLong(op, eq) { %>
|
||||
|
||||
<%
|
||||
if (eq==true) {
|
||||
if (["leq","geq"].indexOf(op) >= 0) retOne();
|
||||
if (["lt","gt"].indexOf(op) >= 0) retZero();
|
||||
}
|
||||
%>
|
||||
|
||||
|
||||
<% const label_gt = global.tmpLabel() %>
|
||||
<% const label_lt = global.tmpLabel() %>
|
||||
<% for (let i=n64-1; i>=0; i--) { %>
|
||||
mov rax, [rsp + <%= 8+(i*8) %>]
|
||||
cmp [half + <%= (i*8) %>], rax ; comare with (q-1)/2
|
||||
jc <%=label_lt%> ; half<rax => e1-e2 is neg => e1 < e2
|
||||
jnz <%=label_gt%> ; half>rax => e1 -e2 is pos => e1 > e2
|
||||
<% } %>
|
||||
; half == rax => e1-e2 is pos => e1 > e2
|
||||
<%=label_gt%>:
|
||||
<% if (["geq","gt"].indexOf(op) >= 0) retOne(); else retZero(); %>
|
||||
<%=label_lt%>:
|
||||
<% if (["leq","lt"].indexOf(op) >= 0) retOne(); else retZero(); %>
|
||||
<% } // cmpLong%>
|
||||
|
||||
<% function cmpOp(op) { %>
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; <%= op %>
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdx <= Pointer to element 2
|
||||
; rdi <= Pointer to result can be zero or one.
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_<%=op%>:
|
||||
sub rsp, <%= (n64+1)*8 %> ; Save space for the result of the substraction
|
||||
push rdi ; Save rdi
|
||||
lea rdi, [rsp+8] ; We pushed rdi so we need to add 8
|
||||
call <%=name%>_sub ; Do a substraction
|
||||
call <%=name%>_toNormal ; Convert it to normal
|
||||
pop rdi
|
||||
|
||||
mov rax, [rsp] ; We already poped do no need to add 8
|
||||
bt rax, 63 ; check is result is long
|
||||
jc <%=op%>_longCmp
|
||||
|
||||
<%=op%>_shortCmp:
|
||||
cmp eax, 0
|
||||
je <%=op%>_s_eq
|
||||
js <%=op%>_s_lt
|
||||
<%=op%>_s_gt:
|
||||
<% if (["geq","gt", "neq"].indexOf(op) >= 0) retOne(); else retZero(); %>
|
||||
<%=op%>_s_lt:
|
||||
<% if (["leq","lt", "neq"].indexOf(op) >= 0) retOne(); else retZero(); %>
|
||||
<%=op%>_s_eq:
|
||||
<% if (["eq","geq", "leq"].indexOf(op) >= 0) retOne(); else retZero(); %>
|
||||
|
||||
<%=op%>_longCmp:
|
||||
|
||||
<% for (let i=n64-1; i>=0; i--) { %>
|
||||
cmp qword [rsp + <%= 8+(i*8) %>], 0
|
||||
jnz <%=op%>_neq
|
||||
<% } %>
|
||||
<%=op%>_eq:
|
||||
<% if (op == "eq") {
|
||||
retOne();
|
||||
} else if (op == "neq") {
|
||||
retZero();
|
||||
} else {
|
||||
cmpLong(op, true);
|
||||
}
|
||||
%>
|
||||
<%=op%>_neq:
|
||||
<% if (op == "neq") {
|
||||
retOne();
|
||||
} else if (op == "eq") {
|
||||
retZero();
|
||||
} else {
|
||||
cmpLong(op, false);
|
||||
}
|
||||
%>
|
||||
|
||||
|
||||
<% } %>
|
||||
|
||||
<%= cmpOp("eq") %>
|
||||
<%= cmpOp("neq") %>
|
||||
<%= cmpOp("lt") %>
|
||||
<%= cmpOp("gt") %>
|
||||
<%= cmpOp("leq") %>
|
||||
<%= cmpOp("geq") %>
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; copy
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Copies
|
||||
; Params:
|
||||
; rsi <= the src
|
||||
; rdi <= the dest
|
||||
;
|
||||
; Nidified registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_copy:
|
||||
<% for (let i=0; i<=n64; i++) { %>
|
||||
mov rax, [rsi + <%= i*8 %>]
|
||||
mov [rdi + <%= i*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; copy an array of integers
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Copies
|
||||
; Params:
|
||||
; rsi <= the src
|
||||
; rdi <= the dest
|
||||
; rdx <= number of integers to copy
|
||||
;
|
||||
; Nidified registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_copyn:
|
||||
<%=name%>_copyn_loop:
|
||||
mov r8, rsi
|
||||
mov r9, rdi
|
||||
mov rax, <%= n64+1 %>
|
||||
mul rdx
|
||||
mov rcx, rax
|
||||
cld
|
||||
rep movsq
|
||||
mov rsi, r8
|
||||
mov rdi, r9
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawCopyS2L
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Convert a 64 bit integer to a long format field element
|
||||
; Params:
|
||||
; rsi <= the integer
|
||||
; rdi <= Pointer to the overwritted element
|
||||
;
|
||||
; Nidified registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
rawCopyS2L:
|
||||
mov al, 0x80
|
||||
shl rax, 56
|
||||
mov [rdi], rax ; set the result to LONG normal
|
||||
|
||||
cmp rsi, 0
|
||||
js u64toLong_adjust_neg
|
||||
|
||||
mov [rdi + 8], rsi
|
||||
xor rax, rax
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov [rdi + <%= 8+i*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
u64toLong_adjust_neg:
|
||||
add rsi, [q] ; Set the first digit
|
||||
mov [rdi + 8], rsi ;
|
||||
|
||||
mov rsi, -1 ; all ones
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rax, rsi ; Add to q
|
||||
adc rax, [q + <%= i*8 %> ]
|
||||
mov [rdi + <%= (i+1)*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; toInt
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Convert a 64 bit integer to a long format field element
|
||||
; Params:
|
||||
; rsi <= Pointer to the element
|
||||
; Returs:
|
||||
; rax <= The value
|
||||
;;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_toInt:
|
||||
mov rax, [rdi]
|
||||
bt rax, 63
|
||||
jc <%=name%>_long
|
||||
movsx rax, eax
|
||||
ret
|
||||
|
||||
<%=name%>_long:
|
||||
mov rax, [rdi + 8]
|
||||
mov rcx, rax
|
||||
shr rcx, 31
|
||||
jnz <%=name%>_longNeg
|
||||
<% for (let i=1; i< n64; i++) { %>
|
||||
mov rcx, [rdi + <%= i*8+8 %>]
|
||||
test rcx, rcx
|
||||
jnz <%=name%>_longNeg
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
<%=name%>_longNeg:
|
||||
mov rax, [rdi + 8]
|
||||
sub rax, [q]
|
||||
jnc <%=name%>_longErr
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rcx, [rdi + <%= i*8+8 %>]
|
||||
sbb rcx, [q + <%= i*8 %>]
|
||||
jnc <%=name%>_longErr
|
||||
<% } %>
|
||||
mov rcx, rax
|
||||
sar rcx, 31
|
||||
add rcx, 1
|
||||
jnz <%=name%>_longErr
|
||||
ret
|
||||
|
||||
<%=name%>_longErr:
|
||||
push rdi
|
||||
mov rdi, 0
|
||||
call <%=name%>_fail
|
||||
pop rdi
|
||||
|
||||
|
||||
|
||||
5841
c/buildasm/fr.asm
5841
c/buildasm/fr.asm
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
||||
|
||||
|
||||
global <%=name%>_copy
|
||||
global <%=name%>_copyn
|
||||
global <%=name%>_add
|
||||
global <%=name%>_sub
|
||||
global <%=name%>_neg
|
||||
global <%=name%>_mul
|
||||
global <%=name%>_square
|
||||
global <%=name%>_band
|
||||
global <%=name%>_bor
|
||||
global <%=name%>_bxor
|
||||
global <%=name%>_bnot
|
||||
global <%=name%>_eq
|
||||
global <%=name%>_neq
|
||||
global <%=name%>_lt
|
||||
global <%=name%>_gt
|
||||
global <%=name%>_leq
|
||||
global <%=name%>_geq
|
||||
global <%=name%>_land
|
||||
global <%=name%>_lor
|
||||
global <%=name%>_lnot
|
||||
global <%=name%>_toNormal
|
||||
global <%=name%>_toLongNormal
|
||||
global <%=name%>_toMontgomery
|
||||
global <%=name%>_toInt
|
||||
global <%=name%>_isTrue
|
||||
global <%=name%>_q
|
||||
extern <%=name%>_fail
|
||||
DEFAULT REL
|
||||
|
||||
section .text
|
||||
<%- include('utils.asm.ejs'); %>
|
||||
<%- include('copy.asm.ejs'); %>
|
||||
<%- include('montgomery.asm.ejs'); %>
|
||||
<%- include('add.asm.ejs'); %>
|
||||
<%- include('sub.asm.ejs'); %>
|
||||
<%- include('neg.asm.ejs'); %>
|
||||
<%- include('mul.asm.ejs'); %>
|
||||
<%- include('binops.asm.ejs'); %>
|
||||
<%- include('cmpops.asm.ejs'); %>
|
||||
<%- include('logicalops.asm.ejs'); %>
|
||||
|
||||
section .data
|
||||
<%=name%>_q:
|
||||
dd 0
|
||||
dd 0x80000000
|
||||
q dq <%= constantElement(q) %>
|
||||
half dq <%= constantElement(q.shiftRight(1)) %>
|
||||
R2 dq <%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>
|
||||
R3 dq <%= constantElement(bigInt.one.shiftLeft(n64*64*3).mod(q)) %>
|
||||
lboMask dq 0x<%= bigInt("8000000000000000",16).shiftRight(n64*64 - q.bitLength()).minus(bigInt.one).toString(16) %>
|
||||
|
||||
185
c/buildasm/fr.c
185
c/buildasm/fr.c
@@ -1,185 +0,0 @@
|
||||
#include "fr.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <gmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
mpz_t q;
|
||||
mpz_t zero;
|
||||
mpz_t one;
|
||||
mpz_t mask;
|
||||
size_t nBits;
|
||||
|
||||
|
||||
void Fr_toMpz(mpz_t r, PFrElement pE) {
|
||||
Fr_toNormal(pE);
|
||||
if (!(pE->type & Fr_LONG)) {
|
||||
mpz_set_si(r, pE->shortVal);
|
||||
if (pE->shortVal<0) {
|
||||
mpz_add(r, r, q);
|
||||
}
|
||||
} else {
|
||||
mpz_import(r, Fr_N64, -1, 8, -1, 0, (const void *)pE->longVal);
|
||||
}
|
||||
}
|
||||
|
||||
void Fr_fromMpz(PFrElement pE, mpz_t v) {
|
||||
if (mpz_fits_sint_p(v)) {
|
||||
pE->type = Fr_SHORT;
|
||||
pE->shortVal = mpz_get_si(v);
|
||||
} else {
|
||||
pE->type = Fr_LONG;
|
||||
for (int i=0; i<Fr_N64; i++) pE->longVal[i] = 0;
|
||||
mpz_export((void *)(pE->longVal), NULL, -1, 8, -1, 0, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Fr_init() {
|
||||
mpz_init(q);
|
||||
mpz_import(q, Fr_N64, -1, 8, -1, 0, (const void *)Fr_q.longVal);
|
||||
mpz_init_set_ui(zero, 0);
|
||||
mpz_init_set_ui(one, 1);
|
||||
nBits = mpz_sizeinbase (q, 2);
|
||||
mpz_init(mask);
|
||||
mpz_mul_2exp(mask, one, nBits-1);
|
||||
mpz_sub(mask, mask, one);
|
||||
|
||||
}
|
||||
|
||||
void Fr_str2element(PFrElement pE, char const *s) {
|
||||
mpz_t mr;
|
||||
mpz_init_set_str(mr, s, 10);
|
||||
Fr_fromMpz(pE, mr);
|
||||
}
|
||||
|
||||
char *Fr_element2str(PFrElement pE) {
|
||||
mpz_t r;
|
||||
if (!(pE->type & Fr_LONG)) {
|
||||
if (pE->shortVal>=0) {
|
||||
char *r = new char[32];
|
||||
sprintf(r, "%d", pE->shortVal);
|
||||
return r;
|
||||
} else {
|
||||
mpz_init_set_si(r, pE->shortVal);
|
||||
mpz_add(r, r, q);
|
||||
mpz_clear(q);
|
||||
}
|
||||
} else {
|
||||
Fr_toNormal(pE);
|
||||
mpz_init(r);
|
||||
mpz_import(r, Fr_N64, -1, 8, -1, 0, (const void *)pE->longVal);
|
||||
}
|
||||
char *res = mpz_get_str (0, 10, r);
|
||||
mpz_clear(r);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Fr_idiv(PFrElement r, PFrElement a, PFrElement b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
Fr_toMpz(ma, a);
|
||||
// char *s1 = mpz_get_str (0, 10, ma);
|
||||
// printf("s1 %s\n", s1);
|
||||
Fr_toMpz(mb, b);
|
||||
// char *s2 = mpz_get_str (0, 10, mb);
|
||||
// printf("s2 %s\n", s2);
|
||||
mpz_fdiv_q(mr, ma, mb);
|
||||
// char *sr = mpz_get_str (0, 10, mr);
|
||||
// printf("r %s\n", sr);
|
||||
Fr_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void Fr_mod(PFrElement r, PFrElement a, PFrElement b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
Fr_toMpz(ma, a);
|
||||
Fr_toMpz(mb, b);
|
||||
mpz_fdiv_r(mr, ma, mb);
|
||||
Fr_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void Fr_shl(PFrElement r, PFrElement a, PFrElement b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
Fr_toMpz(ma, a);
|
||||
Fr_toMpz(mb, b);
|
||||
if (mpz_cmp_ui(mb, nBits) >= 0) {
|
||||
mpz_set(mr, zero);
|
||||
} else {
|
||||
mpz_mul_2exp(mr, ma, mpz_get_ui(mb));
|
||||
mpz_and(mr, mr, mask);
|
||||
}
|
||||
Fr_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void Fr_shr(PFrElement r, PFrElement a, PFrElement b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
Fr_toMpz(ma, a);
|
||||
Fr_toMpz(mb, b);
|
||||
if (mpz_cmp_ui(mb, nBits) >= 0) {
|
||||
mpz_set(mr, zero);
|
||||
} else {
|
||||
mpz_tdiv_q_2exp(mr, ma, mpz_get_ui(mb));
|
||||
mpz_and(mr, mr, mask);
|
||||
}
|
||||
Fr_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
|
||||
void Fr_pow(PFrElement r, PFrElement a, PFrElement b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
Fr_toMpz(ma, a);
|
||||
Fr_toMpz(mb, b);
|
||||
mpz_powm(mr, ma, mb, q);
|
||||
Fr_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void Fr_inv(PFrElement r, PFrElement a) {
|
||||
mpz_t ma;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mr);
|
||||
|
||||
Fr_toMpz(ma, a);
|
||||
mpz_invert(mr, ma, q);
|
||||
Fr_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void Fr_div(PFrElement r, PFrElement a, PFrElement b) {
|
||||
FrElement tmp;
|
||||
Fr_inv(&tmp, b);
|
||||
Fr_mul(r, a, &tmp);
|
||||
}
|
||||
|
||||
void Fr_fail() {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
#include "<%=name.toLowerCase()+".h"%>"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <gmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
mpz_t q;
|
||||
mpz_t zero;
|
||||
mpz_t one;
|
||||
mpz_t mask;
|
||||
size_t nBits;
|
||||
|
||||
|
||||
void <%=name%>_toMpz(mpz_t r, P<%=name%>Element pE) {
|
||||
<%=name%>_toNormal(pE);
|
||||
if (!(pE->type & <%=name%>_LONG)) {
|
||||
mpz_set_si(r, pE->shortVal);
|
||||
if (pE->shortVal<0) {
|
||||
mpz_add(r, r, q);
|
||||
}
|
||||
} else {
|
||||
mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)pE->longVal);
|
||||
}
|
||||
}
|
||||
|
||||
void <%=name%>_fromMpz(P<%=name%>Element pE, mpz_t v) {
|
||||
if (mpz_fits_sint_p(v)) {
|
||||
pE->type = <%=name%>_SHORT;
|
||||
pE->shortVal = mpz_get_si(v);
|
||||
} else {
|
||||
pE->type = <%=name%>_LONG;
|
||||
for (int i=0; i<<%=name%>_N64; i++) pE->longVal[i] = 0;
|
||||
mpz_export((void *)(pE->longVal), NULL, -1, 8, -1, 0, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void <%=name%>_init() {
|
||||
mpz_init(q);
|
||||
mpz_import(q, <%=name%>_N64, -1, 8, -1, 0, (const void *)Fr_q.longVal);
|
||||
mpz_init_set_ui(zero, 0);
|
||||
mpz_init_set_ui(one, 1);
|
||||
nBits = mpz_sizeinbase (q, 2);
|
||||
mpz_init(mask);
|
||||
mpz_mul_2exp(mask, one, nBits-1);
|
||||
mpz_sub(mask, mask, one);
|
||||
|
||||
}
|
||||
|
||||
void <%=name%>_str2element(P<%=name%>Element pE, char const *s) {
|
||||
mpz_t mr;
|
||||
mpz_init_set_str(mr, s, 10);
|
||||
<%=name%>_fromMpz(pE, mr);
|
||||
}
|
||||
|
||||
char *<%=name%>_element2str(P<%=name%>Element pE) {
|
||||
mpz_t r;
|
||||
if (!(pE->type & <%=name%>_LONG)) {
|
||||
if (pE->shortVal>=0) {
|
||||
char *r = new char[32];
|
||||
sprintf(r, "%d", pE->shortVal);
|
||||
return r;
|
||||
} else {
|
||||
mpz_init_set_si(r, pE->shortVal);
|
||||
mpz_add(r, r, q);
|
||||
}
|
||||
} else {
|
||||
<%=name%>_toNormal(pE);
|
||||
mpz_init(r);
|
||||
mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)pE->longVal);
|
||||
}
|
||||
char *res = mpz_get_str (0, 10, r);
|
||||
mpz_clear(r);
|
||||
return res;
|
||||
}
|
||||
|
||||
void <%=name%>_idiv(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
<%=name%>_toMpz(ma, a);
|
||||
// char *s1 = mpz_get_str (0, 10, ma);
|
||||
// printf("s1 %s\n", s1);
|
||||
<%=name%>_toMpz(mb, b);
|
||||
// char *s2 = mpz_get_str (0, 10, mb);
|
||||
// printf("s2 %s\n", s2);
|
||||
mpz_fdiv_q(mr, ma, mb);
|
||||
// char *sr = mpz_get_str (0, 10, mr);
|
||||
// printf("r %s\n", sr);
|
||||
<%=name%>_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void <%=name%>_mod(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
<%=name%>_toMpz(ma, a);
|
||||
<%=name%>_toMpz(mb, b);
|
||||
mpz_fdiv_r(mr, ma, mb);
|
||||
<%=name%>_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void <%=name%>_shl(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
<%=name%>_toMpz(ma, a);
|
||||
<%=name%>_toMpz(mb, b);
|
||||
if (mpz_cmp_ui(mb, nBits) >= 0) {
|
||||
mpz_set(mr, zero);
|
||||
} else {
|
||||
mpz_mul_2exp(mr, ma, mpz_get_ui(mb));
|
||||
mpz_and(mr, mr, mask);
|
||||
}
|
||||
<%=name%>_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void <%=name%>_shr(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
<%=name%>_toMpz(ma, a);
|
||||
<%=name%>_toMpz(mb, b);
|
||||
if (mpz_cmp_ui(mb, nBits) >= 0) {
|
||||
mpz_set(mr, zero);
|
||||
} else {
|
||||
mpz_tdiv_q_2exp(mr, ma, mpz_get_ui(mb));
|
||||
mpz_and(mr, mr, mask);
|
||||
}
|
||||
<%=name%>_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
|
||||
void <%=name%>_pow(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
|
||||
mpz_t ma;
|
||||
mpz_t mb;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mb);
|
||||
mpz_init(mr);
|
||||
|
||||
<%=name%>_toMpz(ma, a);
|
||||
<%=name%>_toMpz(mb, b);
|
||||
mpz_powm(mr, ma, mb, q);
|
||||
<%=name%>_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void <%=name%>_inv(P<%=name%>Element r, P<%=name%>Element a) {
|
||||
mpz_t ma;
|
||||
mpz_t mr;
|
||||
mpz_init(ma);
|
||||
mpz_init(mr);
|
||||
|
||||
<%=name%>_toMpz(ma, a);
|
||||
mpz_invert(mr, ma, q);
|
||||
<%=name%>_fromMpz(r, mr);
|
||||
}
|
||||
|
||||
void <%=name%>_div(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
|
||||
<%=name%>Element tmp;
|
||||
<%=name%>_inv(&tmp, b);
|
||||
<%=name%>_mul(r, a, &tmp);
|
||||
}
|
||||
|
||||
void <%=name%>_fail() {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#ifndef __FR_H
|
||||
#define __FR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#define Fr_N64 4
|
||||
#define Fr_SHORT 0x00000000
|
||||
#define Fr_LONG 0x80000000
|
||||
#define Fr_LONGMONTGOMERY 0xC0000000
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int32_t shortVal;
|
||||
uint32_t type;
|
||||
uint64_t longVal[Fr_N64];
|
||||
} FrElement;
|
||||
typedef FrElement *PFrElement;
|
||||
extern FrElement Fr_q;
|
||||
extern "C" void Fr_copy(PFrElement r, PFrElement a);
|
||||
extern "C" void Fr_copyn(PFrElement r, PFrElement a, int n);
|
||||
extern "C" void Fr_add(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_sub(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_neg(PFrElement r, PFrElement a);
|
||||
extern "C" void Fr_mul(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_square(PFrElement r, PFrElement a);
|
||||
extern "C" void Fr_band(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_bor(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_bxor(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_bnot(PFrElement r, PFrElement a);
|
||||
extern "C" void Fr_eq(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_neq(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_lt(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_gt(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_leq(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_geq(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_land(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_lor(PFrElement r, PFrElement a, PFrElement b);
|
||||
extern "C" void Fr_lnot(PFrElement r, PFrElement a);
|
||||
extern "C" void Fr_toNormal(PFrElement pE);
|
||||
extern "C" void Fr_toLongNormal(PFrElement pE);
|
||||
extern "C" void Fr_toMontgomery(PFrElement pE);
|
||||
|
||||
extern "C" int Fr_isTrue(PFrElement pE);
|
||||
extern "C" int Fr_toInt(PFrElement pE);
|
||||
|
||||
extern "C" void Fr_fail();
|
||||
|
||||
extern FrElement Fr_q;
|
||||
|
||||
// Pending functions to convert
|
||||
|
||||
void Fr_str2element(PFrElement pE, char const*s);
|
||||
char *Fr_element2str(PFrElement pE);
|
||||
void Fr_idiv(PFrElement r, PFrElement a, PFrElement b);
|
||||
void Fr_mod(PFrElement r, PFrElement a, PFrElement b);
|
||||
void Fr_inv(PFrElement r, PFrElement a);
|
||||
void Fr_div(PFrElement r, PFrElement a, PFrElement b);
|
||||
void Fr_shl(PFrElement r, PFrElement a, PFrElement b);
|
||||
void Fr_shr(PFrElement r, PFrElement a, PFrElement b);
|
||||
void Fr_pow(PFrElement r, PFrElement a, PFrElement b);
|
||||
|
||||
|
||||
void Fr_init();
|
||||
|
||||
|
||||
|
||||
#endif // __FR_H
|
||||
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#ifndef __<%=name.toUpperCase()%>_H
|
||||
#define __<%=name.toUpperCase()%>_H
|
||||
|
||||
#include <stdint.h>
|
||||
#define <%=name%>_N64 <%= n64 %>
|
||||
#define <%=name%>_SHORT 0x00000000
|
||||
#define <%=name%>_LONG 0x80000000
|
||||
#define <%=name%>_LONGMONTGOMERY 0xC0000000
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int32_t shortVal;
|
||||
uint32_t type;
|
||||
uint64_t longVal[<%=name%>_N64];
|
||||
} <%=name%>Element;
|
||||
typedef <%=name%>Element *P<%=name%>Element;
|
||||
extern <%=name%>Element <%=name%>_q;
|
||||
extern "C" void <%=name%>_copy(P<%=name%>Element r, P<%=name%>Element a);
|
||||
extern "C" void <%=name%>_copyn(P<%=name%>Element r, P<%=name%>Element a, int n);
|
||||
extern "C" void <%=name%>_add(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_sub(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_neg(P<%=name%>Element r, P<%=name%>Element a);
|
||||
extern "C" void <%=name%>_mul(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_square(P<%=name%>Element r, P<%=name%>Element a);
|
||||
extern "C" void <%=name%>_band(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_bor(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_bxor(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_bnot(P<%=name%>Element r, P<%=name%>Element a);
|
||||
extern "C" void <%=name%>_eq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_neq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_lt(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_gt(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_leq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_geq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_land(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_lor(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
extern "C" void <%=name%>_lnot(P<%=name%>Element r, P<%=name%>Element a);
|
||||
extern "C" void <%=name%>_toNormal(P<%=name%>Element pE);
|
||||
extern "C" void <%=name%>_toLongNormal(P<%=name%>Element pE);
|
||||
extern "C" void <%=name%>_toMontgomery(P<%=name%>Element pE);
|
||||
|
||||
extern "C" int <%=name%>_isTrue(P<%=name%>Element pE);
|
||||
extern "C" int <%=name%>_toInt(P<%=name%>Element pE);
|
||||
|
||||
extern "C" void <%=name%>_fail();
|
||||
|
||||
extern <%=name%>Element <%=name%>_q;
|
||||
|
||||
// Pending functions to convert
|
||||
|
||||
void <%=name%>_str2element(P<%=name%>Element pE, char const*s);
|
||||
char *<%=name%>_element2str(P<%=name%>Element pE);
|
||||
void <%=name%>_idiv(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
void <%=name%>_mod(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
void <%=name%>_inv(P<%=name%>Element r, P<%=name%>Element a);
|
||||
void <%=name%>_div(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
void <%=name%>_shl(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
void <%=name%>_shr(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
void <%=name%>_pow(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b);
|
||||
|
||||
|
||||
void <%=name%>_init();
|
||||
|
||||
|
||||
|
||||
#endif // __<%=name.toUpperCase()%>_H
|
||||
|
||||
|
||||
|
||||
BIN
c/buildasm/fr.o
BIN
c/buildasm/fr.o
Binary file not shown.
@@ -1,97 +0,0 @@
|
||||
|
||||
|
||||
<% function isTrue(resReg, srcPtrReg) { %>
|
||||
<% const longIsZero = global.tmpLabel() %>
|
||||
<% const retOne = global.tmpLabel("retOne") %>
|
||||
<% const retZero = global.tmpLabel("retZero") %>
|
||||
<% const done = global.tmpLabel("done") %>
|
||||
|
||||
mov rax, [<%=srcPtrReg%>]
|
||||
bt rax, 63
|
||||
jc <%= longIsZero %>
|
||||
|
||||
test eax, eax
|
||||
jz <%= retZero %>
|
||||
jmp <%= retOne %>
|
||||
|
||||
<%= longIsZero %>:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [<%= srcPtrReg + " + " +(i*8+8) %>]
|
||||
test rax, rax
|
||||
jnz <%= retOne %>
|
||||
<% } %>
|
||||
|
||||
<%= retZero %>:
|
||||
mov qword <%=resReg%>, 0
|
||||
jmp <%= done %>
|
||||
|
||||
<%= retOne %>:
|
||||
mov qword <%=resReg%>, 1
|
||||
|
||||
<%= done %>:
|
||||
<% } %>
|
||||
|
||||
|
||||
|
||||
|
||||
<% function logicalOp(op) { %>
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; l<%= op %>
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Logical <%= op %> between two elements
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdx <= Pointer to element 2
|
||||
; rdi <= Pointer to result zero or one
|
||||
; Modified Registers:
|
||||
; rax, rcx, r8
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_l<%=op%>:
|
||||
<%= isTrue("r8", "rsi") %>
|
||||
<%= isTrue("rcx", "rdx") %>
|
||||
<%=op%> rcx, r8
|
||||
mov [rdi], rcx
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
<% logicalOp("and"); %>
|
||||
<% logicalOp("or"); %>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; lnot
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Do the logical not of an element
|
||||
; Params:
|
||||
; rsi <= Pointer to element to be tested
|
||||
; rdi <= Pointer to result one if element1 is zero and zero otherwise
|
||||
; Modified Registers:
|
||||
; rax, rax, r8
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_lnot:
|
||||
<%= isTrue("rcx", "rsi") %>
|
||||
test rcx, rcx
|
||||
|
||||
jz lnot_retOne
|
||||
lnot_retZero:
|
||||
mov qword [rdi], 0
|
||||
ret
|
||||
lnot_retOne:
|
||||
mov qword [rdi], 1
|
||||
ret
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; isTrue
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Convert a 64 bit integer to a long format field element
|
||||
; Params:
|
||||
; rsi <= Pointer to the element
|
||||
; Returs:
|
||||
; rax <= 1 if true 0 if false
|
||||
;;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_isTrue:
|
||||
<%= isTrue("rax", "rdi") %>
|
||||
ret
|
||||
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "fr.h"
|
||||
|
||||
int main() {
|
||||
Fr_init();
|
||||
/*
|
||||
FrElement a = { 0, Fr_LONGMONTGOMERY, {1,1,1,1}};
|
||||
FrElement b = { 0, Fr_LONGMONTGOMERY, {2,2,2,2}};
|
||||
|
||||
|
||||
FrElement a={0x43e1f593f0000000ULL,0x2833e84879b97091ULL,0xb85045b68181585dULL,0x30644e72e131a029ULL};
|
||||
FrElement b = {3,0,0,0};
|
||||
|
||||
FrElement c;
|
||||
*/
|
||||
// Fr_add(&(c[0]), a, a);
|
||||
// Fr_add(&(c[0]), c, b);
|
||||
|
||||
/*
|
||||
for (int i=0; i<1000000000; i++) {
|
||||
Fr_mul(&c, &a, &b);
|
||||
}
|
||||
|
||||
Fr_mul(&c,&a, &b);
|
||||
*/
|
||||
|
||||
/*
|
||||
FrElement a1[10];
|
||||
FrElement a2[10];
|
||||
for (int i=0; i<10; i++) {
|
||||
a1[i].type = Fr_LONGMONTGOMERY;
|
||||
a1[i].shortVal =0;
|
||||
for (int j=0; j<Fr_N64; j++) {
|
||||
a2[i].longVal[j] = i;
|
||||
}
|
||||
}
|
||||
|
||||
Fr_copyn(a2, a1, 10);
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
char *c1 = Fr_element2str(&a1[i]);
|
||||
char *c2 = Fr_element2str(&a2[i]);
|
||||
printf("%s\n%s\n\n", c1, c2);
|
||||
free(c1);
|
||||
free(c2);
|
||||
}
|
||||
*/
|
||||
|
||||
int tests[7] = { 0, 1, 2, -1, -2, 0x7FFFFFFF, (int)0x80000000};
|
||||
for (int i=0; i<7;i++) {
|
||||
FrElement a = { tests[i], Fr_SHORT, {0,0,0,0}};
|
||||
Fr_toLongNormal(&a);
|
||||
int b = Fr_toInt(&a);
|
||||
int c = Fr_isTrue(&a);
|
||||
printf("%d, %d, %d\n", tests[i], b, c);
|
||||
}
|
||||
|
||||
FrElement err = { 0, Fr_LONGMONTGOMERY, {1,1,1,1}};
|
||||
Fr_toInt(&err);
|
||||
|
||||
// printf("%llu, %llu, %llu, %llu\n", c.longVal[0], c.longVal[1], c.longVal[2], c.longVal[3]);
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
|
||||
|
||||
|
||||
<%
|
||||
//////////////////////
|
||||
// montgomeryTemplate
|
||||
//////////////////////
|
||||
// This function creates functions with the montgomery transformation
|
||||
// applied
|
||||
// the round hook allows to add diferent code in the iteration
|
||||
//
|
||||
// All the montgomery functions modifies:
|
||||
// r8, r9, 10, r11, rax, rcx
|
||||
//////////////////////
|
||||
function montgomeryTemplate(fnName, round) {
|
||||
let r0, r1, r2;
|
||||
function setR(step) {
|
||||
if ((step % 3) == 0) {
|
||||
r0 = "r8";
|
||||
r1 = "r9";
|
||||
r2 = "r10";
|
||||
} else if ((step % 3) == 1) {
|
||||
r0 = "r9";
|
||||
r1 = "r10";
|
||||
r2 = "r8";
|
||||
} else {
|
||||
r0 = "r10";
|
||||
r1 = "r8";
|
||||
r2 = "r9";
|
||||
}
|
||||
}
|
||||
|
||||
const base = bigInt.one.shiftLeft(64);
|
||||
const np64 = base.minus(q.modInv(base));
|
||||
%>
|
||||
<%=fnName%>:
|
||||
sub rsp, <%= n64*8 %> ; Reserve space for ms
|
||||
mov rcx, rdx ; rdx is needed for multiplications so keep it in cx
|
||||
mov r11, 0x<%= np64.toString(16) %> ; np
|
||||
xor r8,r8
|
||||
xor r9,r9
|
||||
xor r10,r10
|
||||
<%
|
||||
// Main loop
|
||||
for (let i=0; i<n64*2; i++) {
|
||||
setR(i);
|
||||
round(i, r0, r1, r2);
|
||||
%>
|
||||
|
||||
<%
|
||||
for (let j=i-1; j>=0; j--) { // All ms
|
||||
if (((i-j)<n64)&&(j<n64)) {
|
||||
%>
|
||||
mov rax, [rsp + <%= j*8 %>]
|
||||
mul qword [q + <%= (i-j)*8 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
}
|
||||
} // ms
|
||||
%>
|
||||
|
||||
<%
|
||||
if (i<n64) {
|
||||
%>
|
||||
mov rax, <%= r0 %>
|
||||
mul r11
|
||||
mov [rsp + <%= i*8 %>], rax
|
||||
mul qword [q]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
mov [rdi + <%= (i-n64)*8 %> ], <%= r0 %>
|
||||
xor <%= r0 %>,<%= r0 %>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<%
|
||||
} // Main Loop
|
||||
%>
|
||||
test <%= r1 %>, <%= r1 %>
|
||||
jnz <%=fnName%>_mulM_sq
|
||||
; Compare with q
|
||||
<%
|
||||
for (let i=0; i<n64; i++) {
|
||||
%>
|
||||
mov rax, [rdi + <%= (n64-i-1)*8 %>]
|
||||
cmp rax, [q + <%= (n64-i-1)*8 %>]
|
||||
jc <%=fnName%>_mulM_done ; q is bigget so done.
|
||||
jnz <%=fnName%>_mulM_sq ; q is lower
|
||||
<%
|
||||
}
|
||||
%>
|
||||
; If equal substract q
|
||||
|
||||
<%=fnName%>_mulM_sq:
|
||||
<%
|
||||
for (let i=0; i<n64; i++) {
|
||||
%>
|
||||
mov rax, [q + <%= i*8 %>]
|
||||
<%= i==0 ? "sub" : "sbb" %> [rdi + <%= i*8 %>], rax
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<%=fnName%>_mulM_done:
|
||||
mov rdx, rcx ; recover rdx to its original place.
|
||||
add rsp, <%= n64*8 %> ; recover rsp
|
||||
ret
|
||||
|
||||
<%
|
||||
} // Template
|
||||
%>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawMontgomeryMul
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Multiply two elements in montgomery form
|
||||
; Params:
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
; rdx <= Pointer to the long data of element 2
|
||||
; rdi <= Pointer to the long data of result
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%
|
||||
montgomeryTemplate("rawMontgomeryMul", function(i, r0, r1, r2) {
|
||||
// Same Digit
|
||||
for (let o1=Math.max(0, i-n64+1); (o1<=i)&&(o1<n64); o1++) {
|
||||
const o2= i-o1;
|
||||
%>
|
||||
mov rax, [rsi + <%= 8*o1 %>]
|
||||
mul qword [rcx + <%= 8*o2 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} // Same digit
|
||||
})
|
||||
%>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawMontgomerySquare
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Square an element
|
||||
; Params:
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
; rdi <= Pointer to the long data of result
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%
|
||||
montgomeryTemplate("rawMontgomerySquare", function(i, r0, r1, r2) {
|
||||
// Same Digit
|
||||
for (let o1=Math.max(0, i-n64+1); (o1<((i+1)>>1) )&&(o1<n64); o1++) {
|
||||
const o2= i-o1;
|
||||
%>
|
||||
mov rax, [rsi + <%= 8*o1 %>]
|
||||
mul qword [rsi + <%= 8*o2 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} // Same digit
|
||||
%>
|
||||
|
||||
<% if (i%2 == 0) { %>
|
||||
mov rax, [rsi + <%= 8*(i/2) %>]
|
||||
mul rax
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<% } %>
|
||||
|
||||
<%
|
||||
})
|
||||
%>
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawMontgomeryMul1
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Multiply two elements in montgomery form
|
||||
; Params:
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
; rdx <= second operand
|
||||
; rdi <= Pointer to the long data of result
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%
|
||||
montgomeryTemplate("rawMontgomeryMul1", function(i, r0, r1, r2) {
|
||||
// Same Digit
|
||||
if (i<n64) {
|
||||
%>
|
||||
mov rax, [rsi + <%= 8*i %>]
|
||||
mul rcx
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} // Same digit
|
||||
})
|
||||
%>
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawFromMontgomery
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Multiply two elements in montgomery form
|
||||
; Params:
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
; rdi <= Pointer to the long data of result
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%
|
||||
montgomeryTemplate("rawFromMontgomery", function(i, r0, r1, r2) {
|
||||
// Same Digit
|
||||
if (i<n64) {
|
||||
%>
|
||||
add <%= r0 %>, [rdi + <%= 8*i %>]
|
||||
adc <%= r1 %>, 0x0
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} // Same digit
|
||||
})
|
||||
%>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; toMontgomery
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Convert a number to Montgomery
|
||||
; rdi <= Pointer element to convert
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_toMontgomery:
|
||||
mov rax, [rdi]
|
||||
bts rax, 62 ; check if montgomery
|
||||
jc toMontgomery_doNothing
|
||||
bts rax, 63
|
||||
jc toMontgomeryLong
|
||||
|
||||
toMontgomeryShort:
|
||||
mov [rdi], rax
|
||||
add rdi, 8
|
||||
push rsi
|
||||
lea rsi, [R2]
|
||||
movsx rdx, eax
|
||||
cmp rdx, 0
|
||||
js negMontgomeryShort
|
||||
posMontgomeryShort:
|
||||
call rawMontgomeryMul1
|
||||
pop rsi
|
||||
sub rdi, 8
|
||||
ret
|
||||
|
||||
negMontgomeryShort:
|
||||
neg rdx ; Do the multiplication positive and then negate the result.
|
||||
call rawMontgomeryMul1
|
||||
mov rsi, rdi
|
||||
call rawNegL
|
||||
pop rsi
|
||||
sub rdi, 8
|
||||
ret
|
||||
|
||||
|
||||
toMontgomeryLong:
|
||||
mov [rdi], rax
|
||||
add rdi, 8
|
||||
push rsi
|
||||
mov rdx, rdi
|
||||
lea rsi, [R2]
|
||||
call rawMontgomeryMul
|
||||
pop rsi
|
||||
sub rdi, 8
|
||||
|
||||
toMontgomery_doNothing:
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; toNormal
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Convert a number from Montgomery
|
||||
; rdi <= Pointer element to convert
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_toNormal:
|
||||
mov rax, [rdi]
|
||||
btc rax, 62 ; check if montgomery
|
||||
jnc toNormal_doNothing
|
||||
bt rax, 63 ; if short, it means it's converted
|
||||
jnc toNormal_doNothing
|
||||
|
||||
toNormalLong:
|
||||
mov [rdi], rax
|
||||
add rdi, 8
|
||||
call rawFromMontgomery
|
||||
sub rdi, 8
|
||||
|
||||
toNormal_doNothing:
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; toLongNormal
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Convert a number to long normal
|
||||
; rdi <= Pointer element to convert
|
||||
; Modified registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_toLongNormal:
|
||||
mov rax, [rdi]
|
||||
bt rax, 62 ; check if montgomery
|
||||
jc toLongNormal_fromMontgomery
|
||||
bt rax, 63 ; check if long
|
||||
jnc toLongNormal_fromShort
|
||||
ret ; It is already long
|
||||
|
||||
toLongNormal_fromMontgomery:
|
||||
add rdi, 8
|
||||
call rawFromMontgomery
|
||||
sub rdi, 8
|
||||
ret
|
||||
|
||||
toLongNormal_fromShort:
|
||||
mov r8, rsi ; save rsi
|
||||
movsx rsi, eax
|
||||
call rawCopyS2L
|
||||
mov rsi, r8 ; recover rsi
|
||||
ret
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
<% function mulS1S2() { %>
|
||||
xor rax, rax
|
||||
mov eax, r8d
|
||||
imul r9d
|
||||
jo mul_manageOverflow ; rsi already is the 64bits result
|
||||
|
||||
mov [rdi], rax ; not necessary to adjust so just save and return
|
||||
|
||||
mul_manageOverflow: ; Do the operation in 64 bits
|
||||
push rsi
|
||||
movsx rax, r8d
|
||||
movsx rcx, r9d
|
||||
imul rcx
|
||||
mov rsi, rax
|
||||
call rawCopyS2L
|
||||
pop rsi
|
||||
<% } %>
|
||||
|
||||
<% function squareS1() { %>
|
||||
xor rax, rax
|
||||
mov eax, r8d
|
||||
imul eax
|
||||
jo square_manageOverflow ; rsi already is the 64bits result
|
||||
|
||||
mov [rdi], rax ; not necessary to adjust so just save and return
|
||||
|
||||
square_manageOverflow: ; Do the operation in 64 bits
|
||||
push rsi
|
||||
movsx rax, r8d
|
||||
imul rax
|
||||
mov rsi, rax
|
||||
call rawCopyS2L
|
||||
pop rsi
|
||||
<% } %>
|
||||
|
||||
|
||||
<% function mulL1S2(t) { %>
|
||||
push rsi
|
||||
add rsi, 8
|
||||
movsx rdx, r9d
|
||||
add rdi, 8
|
||||
cmp rdx, 0
|
||||
<% const rawPositiveLabel = global.tmpLabel() %>
|
||||
jns <%= rawPositiveLabel %>
|
||||
neg rdx
|
||||
call rawMontgomeryMul1
|
||||
mov rsi, rdi
|
||||
call rawNegL
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
<% const done = global.tmpLabel() %>
|
||||
jmp <%= done %>
|
||||
<%= rawPositiveLabel %>:
|
||||
call rawMontgomeryMul1
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
<%= done %>:
|
||||
|
||||
<% } %>
|
||||
|
||||
<% function mulS1L2() { %>
|
||||
push rsi
|
||||
lea rsi, [rdx + 8]
|
||||
movsx rdx, r8d
|
||||
add rdi, 8
|
||||
cmp rdx, 0
|
||||
<% const rawPositiveLabel = global.tmpLabel() %>
|
||||
jns <%= rawPositiveLabel %>
|
||||
neg rdx
|
||||
call rawMontgomeryMul1
|
||||
mov rsi, rdi
|
||||
call rawNegL
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
<% const done = global.tmpLabel() %>
|
||||
jmp <%= done %>
|
||||
<%= rawPositiveLabel %>:
|
||||
call rawMontgomeryMul1
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
<%= done %>:
|
||||
|
||||
<% } %>
|
||||
|
||||
<% function mulL1L2() { %>
|
||||
add rdi, 8
|
||||
add rsi, 8
|
||||
add rdx, 8
|
||||
call rawMontgomeryMul
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
<% } %>
|
||||
|
||||
|
||||
<% function squareL1() { %>
|
||||
add rdi, 8
|
||||
add rsi, 8
|
||||
call rawMontgomerySquare
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
<% } %>
|
||||
|
||||
<% function mulR3() { %>
|
||||
push rsi
|
||||
add rdi, 8
|
||||
mov rsi, rdi
|
||||
lea rdx, [R3]
|
||||
call rawMontgomeryMul
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
<% } %>
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; square
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Squares a field element
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdi <= Pointer to result
|
||||
; [rdi] = [rsi] * [rsi]
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_square:
|
||||
mov r8, [rsi]
|
||||
bt r8, 63 ; Check if is short first operand
|
||||
jc square_l1
|
||||
|
||||
square_s1: ; Both operands are short
|
||||
<%= squareS1() %>
|
||||
ret
|
||||
|
||||
square_l1:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jc square_l1m
|
||||
square_l1n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= squareL1() %>
|
||||
<%= mulR3() %>
|
||||
ret
|
||||
|
||||
square_l1m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= squareL1() %>
|
||||
ret
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Multiplies two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdx <= Pointer to element 2
|
||||
; rdi <= Pointer to result
|
||||
; [rdi] = [rsi] * [rdi]
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_mul:
|
||||
mov r8, [rsi]
|
||||
mov r9, [rdx]
|
||||
bt r8, 63 ; Check if is short first operand
|
||||
jc mul_l1
|
||||
bt r9, 63 ; Check if is short second operand
|
||||
jc mul_s1l2
|
||||
|
||||
mul_s1s2: ; Both operands are short
|
||||
<%= mulS1S2() %>
|
||||
ret
|
||||
|
||||
mul_l1:
|
||||
bt r9, 63 ; Check if is short second operand
|
||||
jc mul_l1l2
|
||||
|
||||
;;;;;;;;
|
||||
mul_l1s2:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jc mul_l1ms2
|
||||
mul_l1ns2:
|
||||
bt r9, 62 ; check if montgomery first
|
||||
jc mul_l1ns2m
|
||||
mul_l1ns2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= mulL1S2() %>
|
||||
<%= mulR3() %>
|
||||
ret
|
||||
|
||||
|
||||
mul_l1ns2m:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
|
||||
mul_l1ms2:
|
||||
bt r9, 62 ; check if montgomery second
|
||||
jc mul_l1ms2m
|
||||
mul_l1ms2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= mulL1S2() %>
|
||||
ret
|
||||
|
||||
mul_l1ms2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
|
||||
;;;;;;;;
|
||||
mul_s1l2:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jc mul_s1ml2
|
||||
mul_s1nl2:
|
||||
bt r9, 62 ; check if montgomery first
|
||||
jc mul_s1nl2m
|
||||
mul_s1nl2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= mulS1L2() %>
|
||||
<%= mulR3() %>
|
||||
ret
|
||||
|
||||
mul_s1nl2m:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= mulS1L2(); %>
|
||||
ret
|
||||
|
||||
mul_s1ml2:
|
||||
bt r9, 62 ; check if montgomery first
|
||||
jc mul_s1ml2m
|
||||
mul_s1ml2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
mul_s1ml2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
;;;;
|
||||
mul_l1l2:
|
||||
bt r8, 62 ; check if montgomery first
|
||||
jc mul_l1ml2
|
||||
mul_l1nl2:
|
||||
bt r9, 62 ; check if montgomery second
|
||||
jc mul_l1nl2m
|
||||
mul_l1nl2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= mulL1L2() %>
|
||||
<%= mulR3() %>
|
||||
ret
|
||||
|
||||
mul_l1nl2m:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
mul_l1ml2:
|
||||
bt r9, 62 ; check if montgomery seconf
|
||||
jc mul_l1ml2m
|
||||
mul_l1ml2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
mul_l1ml2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= mulL1L2() %>
|
||||
ret
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
<% function negS() { %>
|
||||
neg eax
|
||||
jo neg_manageOverflow ; Check if overflow. (0x80000000 is the only case)
|
||||
|
||||
mov [rdi], rax ; not necessary to adjust so just save and return
|
||||
ret
|
||||
|
||||
neg_manageOverflow: ; Do the operation in 64 bits
|
||||
push rsi
|
||||
movsx rsi, eax
|
||||
neg rsi
|
||||
call rawCopyS2L
|
||||
pop rsi
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
<% function negL() { %>
|
||||
add rdi, 8
|
||||
add rsi, 8
|
||||
call rawNegL
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; neg
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Adds two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element to be negated
|
||||
; rdi <= Pointer to result
|
||||
; [rdi] = -[rsi]
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_neg:
|
||||
mov rax, [rsi]
|
||||
bt rax, 63 ; Check if is short first operand
|
||||
jc neg_l
|
||||
|
||||
neg_s: ; Operand is short
|
||||
<%= negS() %>
|
||||
|
||||
|
||||
neg_l:
|
||||
mov [rdi], rax ; Copy the type
|
||||
<%= negL() %>
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawNeg
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Negates a value
|
||||
; Params:
|
||||
; rdi <= Pointer to the long data of result
|
||||
; rsi <= Pointer to the long data of element 1
|
||||
;
|
||||
; [rdi] = - [rsi]
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawNegL:
|
||||
; Compare is zero
|
||||
|
||||
xor rax, rax
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
cmp [rsi + <%=i*8%>], rax
|
||||
jnz doNegate
|
||||
<% } %>
|
||||
; it's zero so just set to zero
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
ret
|
||||
doNegate:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "sub" : "sbb" %> rax, [rsi + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
ret
|
||||
@@ -1,33 +0,0 @@
|
||||
const tester = require("../c/buildasm/buildzqfieldtester2.js");
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
|
||||
describe("basic cases", function () {
|
||||
this.timeout(100000);
|
||||
it("should do basic tests", async () => {
|
||||
await tester(__P__, [
|
||||
["add", 0, 0],
|
||||
["add", 0, 1],
|
||||
["add", 1, 0],
|
||||
["add", 1, 1],
|
||||
["add", 2, 1],
|
||||
["add", 2, 10],
|
||||
["add", -1, -1],
|
||||
["add", -20, -10],
|
||||
["add", "10604728079509999371218483608188593244163417117449316147628604036713980815027", "10604728079509999371218483608188593244163417117449316147628604036713980815027"],
|
||||
|
||||
["mul", 0, 0],
|
||||
["mul", 0, 1],
|
||||
["mul", 1, 0],
|
||||
["mul", 1, 1],
|
||||
["mul", 2, 1],
|
||||
["mul", 2, 10],
|
||||
["mul", -1, -1],
|
||||
["mul", -20, -10],
|
||||
["mul", "10604728079509999371218483608188593244163417117449316147628604036713980815027", "10604728079509999371218483608188593244163417117449316147628604036713980815027"],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,209 +0,0 @@
|
||||
const bigInt=require("big-integer");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ZqBuilder {
|
||||
constructor(q, name) {
|
||||
this.q=bigInt(q);
|
||||
this.h = [];
|
||||
this.c = [];
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
build() {
|
||||
this._buildHeaders();
|
||||
this._buildAdd();
|
||||
this._buildMul();
|
||||
|
||||
this.c.push(""); this.h.push("");
|
||||
return [this.h.join("\n"), this.c.join("\n")];
|
||||
}
|
||||
|
||||
_buildHeaders() {
|
||||
this.n64 = Math.floor((this.q.bitLength() - 1) / 64)+1;
|
||||
this.h.push("typedef unsigned long long u64;");
|
||||
this.h.push(`typedef u64 ${this.name}Element[${this.n64}];`);
|
||||
this.h.push(`typedef u64 *P${this.name}Element;`);
|
||||
this.h.push(`extern ${this.name}Element ${this.name}_q;`);
|
||||
this.h.push(`#define ${this.name}_N64 ${this.n64}`);
|
||||
this.c.push(`#include "${this.name.toLowerCase()}.h"`);
|
||||
this._defineConstant(`${this.name}_q`, this.q);
|
||||
this.c.push(""); this.h.push("");
|
||||
}
|
||||
|
||||
_defineConstant(n, v) {
|
||||
let S = `${this.name}Element ${n}={`;
|
||||
const mask = bigInt("FFFFFFFFFFFFFFFF", 16);
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
if (i>0) S = S+",";
|
||||
let shex = v.shiftRight(i*64).and(mask).toString(16);
|
||||
while (shex <16) shex = "0" + shex;
|
||||
S = S + "0x" + shex + "ULL";
|
||||
}
|
||||
S += "};";
|
||||
this.c.push(S);
|
||||
}
|
||||
|
||||
_buildAdd() {
|
||||
this.h.push(`void ${this.name}_add(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b);`);
|
||||
this.c.push(`void ${this.name}_add(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b) {`);
|
||||
this.c.push(" __asm__ __volatile__ (");
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
this.c.push(` "movq ${i*8}(%2), %%rax;"`);
|
||||
this.c.push(` "${i==0 ? "addq" : "adcq"} ${i*8}(%1), %%rax;"`);
|
||||
this.c.push(` "movq %%rax, ${i*8}(%0);"`);
|
||||
}
|
||||
this.c.push(" \"jc SQ;\"");
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
if (i>0) {
|
||||
this.c.push(` "movq ${(this.n64 - i-1)*8}(%0), %%rax;"`);
|
||||
}
|
||||
this.c.push(` "cmp ${(this.n64 - i-1)*8}(%3), %%rax;"`);
|
||||
this.c.push(" \"jg SQ;\"");
|
||||
this.c.push(" \"jl DONE;\"");
|
||||
}
|
||||
this.c.push(" \"SQ:\"");
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
this.c.push(` "movq ${i*8}(%3), %%rax;"`);
|
||||
this.c.push(` "${i==0 ? "subq" : "sbbq"} %%rax, ${i*8}(%0);"`);
|
||||
}
|
||||
this.c.push(" \"DONE:\"");
|
||||
this.c.push(` :: "r" (r), "r" (a), "r" (b), "r" (${this.name}_q) : "%rax", "memory");`);
|
||||
this.c.push("}\n");
|
||||
}
|
||||
|
||||
_buildMul() {
|
||||
|
||||
let r0, r1, r2;
|
||||
function setR(step) {
|
||||
if ((step % 3) == 0) {
|
||||
r0 = "%%r8";
|
||||
r1 = "%%r9";
|
||||
r2 = "%%r10";
|
||||
} else if ((step % 3) == 1) {
|
||||
r0 = "%%r9";
|
||||
r1 = "%%r10";
|
||||
r2 = "%%r8";
|
||||
} else {
|
||||
r0 = "%%r10";
|
||||
r1 = "%%r8";
|
||||
r2 = "%%r9";
|
||||
}
|
||||
}
|
||||
const base = bigInt.one.shiftLeft(64);
|
||||
const np64 = base.minus(this.q.modInv(base));
|
||||
|
||||
this.h.push(`void ${this.name}_mul(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b);`);
|
||||
this.c.push(`void ${this.name}_mul(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b) {`);
|
||||
this.c.push(" __asm__ __volatile__ (");
|
||||
|
||||
this.c.push(` "subq $${this.n64*8}, %%rsp;"`);
|
||||
this.c.push(` "movq $0x${np64.toString(16)}, %%r11;"`);
|
||||
this.c.push(" \"movq $0x0, %%r8;\"");
|
||||
this.c.push(" \"movq $0x0, %%r9;\"");
|
||||
this.c.push(" \"movq $0x0, %%r10;\"");
|
||||
|
||||
for (let i=0; i<this.n64*2; i++) {
|
||||
setR(i);
|
||||
|
||||
for (let o1=Math.max(0, i-this.n64+1); (o1<=i)&&(o1<this.n64); o1++) {
|
||||
const o2= i-o1;
|
||||
this.c.push(` "movq ${o1*8}(%1), %%rax;"`);
|
||||
this.c.push(` "mulq ${o2*8}(%2);"`);
|
||||
this.c.push(` "addq %%rax, ${r0};"`);
|
||||
this.c.push(` "adcq %%rdx, ${r1};"`);
|
||||
this.c.push(` "adcq $0x0, ${r2};"`);
|
||||
}
|
||||
|
||||
for (let j=i-1; j>=0; j--) {
|
||||
if (((i-j)<this.n64)&&(j<this.n64)) {
|
||||
this.c.push(` "movq ${j*8}(%%rsp), %%rax;"`);
|
||||
this.c.push(` "mulq ${(i-j)*8}(%3);"`);
|
||||
this.c.push(` "addq %%rax, ${r0};"`);
|
||||
this.c.push(` "adcq %%rdx, ${r1};"`);
|
||||
this.c.push(` "adcq $0x0, ${r2};"`);
|
||||
}
|
||||
}
|
||||
|
||||
if (i<this.n64) {
|
||||
this.c.push(` "movq ${r0}, %%rax;"`);
|
||||
this.c.push(" \"mulq %%r11;\"");
|
||||
this.c.push(` "movq %%rax, ${i*8}(%%rsp);"`);
|
||||
this.c.push(" \"mulq (%3);\"");
|
||||
this.c.push(` "addq %%rax, ${r0};"`);
|
||||
this.c.push(` "adcq %%rdx, ${r1};"`);
|
||||
this.c.push(` "adcq $0x0, ${r2};"`);
|
||||
} else {
|
||||
this.c.push(` "movq ${r0}, ${(i-this.n64)*8}(%0);"`);
|
||||
this.c.push(` "movq $0, ${r0};"`);
|
||||
}
|
||||
}
|
||||
|
||||
this.c.push(` "cmp $0, ${r1};"`);
|
||||
this.c.push(" \"jne SQ2;\"");
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
this.c.push(` "movq ${(this.n64 - i-1)*8}(%0), %%rax;"`);
|
||||
this.c.push(` "cmp ${(this.n64 - i-1)*8}(%3), %%rax;"`);
|
||||
this.c.push(" \"jg SQ2;\"");
|
||||
this.c.push(" \"jl DONE2;\"");
|
||||
}
|
||||
this.c.push(" \"SQ2:\"");
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
this.c.push(` "movq ${i*8}(%3), %%rax;"`);
|
||||
this.c.push(` "${i==0 ? "subq" : "sbbq"} %%rax, ${i*8}(%0);"`);
|
||||
}
|
||||
this.c.push(" \"DONE2:\"");
|
||||
this.c.push(` "addq $${this.n64*8}, %%rsp;"`);
|
||||
|
||||
this.c.push(` :: "r" (r), "r" (a), "r" (b), "r" (${this.name}_q) : "%rax", "%rdx", "%r8", "%r9", "%r10", "%r11", "memory");`);
|
||||
this.c.push("}\n");
|
||||
}
|
||||
|
||||
_buildIDiv() {
|
||||
this.h.push(`void ${this.name}_idiv(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b);`);
|
||||
this.c.push(`void ${this.name}_idiv(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b) {`);
|
||||
this.c.push(" __asm__ __volatile__ (");
|
||||
this.c.push(" \"pxor %%xmm0, %%xmm0;\""); // Comparison Register
|
||||
if (this.n64 == 1) {
|
||||
this.c.push(` "mov %%rax, $${this.n64 - 8};"`);
|
||||
|
||||
} else {
|
||||
this.c.push(` "mov %%rax, $${this.n64 -16};"`);
|
||||
}
|
||||
|
||||
this.c.push(` :: "r" (r), "r" (a), "r" (b), "r" (${this.name}_q) : "%rax", "%rdx", "%r8", "%r9", "%r10", "%r11", "memory");`);
|
||||
this.c.push("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
var runningAsScript = !module.parent;
|
||||
|
||||
if (runningAsScript) {
|
||||
const fs = require("fs");
|
||||
var argv = require("yargs")
|
||||
.usage("Usage: $0 -q [primeNum] -n [name] -oc [out .c file] -oh [out .h file]")
|
||||
.demandOption(["q","n"])
|
||||
.alias("q", "prime")
|
||||
.alias("n", "name")
|
||||
.argv;
|
||||
|
||||
const q = bigInt(argv.q);
|
||||
|
||||
const cFileName = (argv.oc) ? argv.oc : argv.name.toLowerCase() + ".c";
|
||||
const hFileName = (argv.oh) ? argv.oh : argv.name.toLowerCase() + ".h";
|
||||
|
||||
const builder = new ZqBuilder(q, argv.name);
|
||||
|
||||
const res = builder.build();
|
||||
|
||||
fs.writeFileSync(hFileName, res[0], "utf8");
|
||||
fs.writeFileSync(cFileName, res[1], "utf8");
|
||||
} else {
|
||||
module.exports = function(q, name) {
|
||||
const builder = new ZqBuilder(q, name);
|
||||
return builder.build();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
const chai = require("chai");
|
||||
const assert = chai.assert;
|
||||
|
||||
const fs = require("fs");
|
||||
var tmp = require("tmp-promise");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const BuildZqField = require("./buildzqfield");
|
||||
const ZqField = require("fflib").ZqField;
|
||||
|
||||
module.exports = testField;
|
||||
|
||||
function toMontgomeryStr(a, prime) {
|
||||
const n64 = Math.floor((prime.bitLength() - 1) / 64)+1;
|
||||
return a.shiftLeft(n64*64).mod(prime).toString(10);
|
||||
}
|
||||
|
||||
function fromMontgomeryStr(a, prime) {
|
||||
const n64 = Math.floor((prime.bitLength() - 1) / 64)+1;
|
||||
const R = bigInt.one.shiftLeft(n64*64).mod(prime);
|
||||
const RI = R.modInv(prime);
|
||||
return bigInt(a).times(RI).mod(prime);
|
||||
}
|
||||
|
||||
|
||||
async function testField(prime, test) {
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
const F = new ZqField(prime);
|
||||
|
||||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
|
||||
|
||||
const [hSource, cSource] = BuildZqField(prime, "Fr");
|
||||
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.h"), hSource, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.c"), cSource, "utf8");
|
||||
|
||||
await exec("g++" +
|
||||
` ${path.join(__dirname, "tester.c")}` +
|
||||
` ${path.join(dir.path, "fr.c")}` +
|
||||
` -o ${path.join(dir.path, "tester")}` +
|
||||
" -lgmp"
|
||||
);
|
||||
|
||||
for (let i=0; i<test.length; i++) {
|
||||
let a = bigInt(test[i][1]).mod(prime);
|
||||
if (a.isNegative()) a = prime.add(a);
|
||||
let b = bigInt(test[i][2]).mod(prime);
|
||||
if (b.isNegative()) b = prime.add(b);
|
||||
const ec = F[test[i][0]](a,b);
|
||||
// console.log(toMontgomeryStr(a, prime));
|
||||
// console.log(toMontgomeryStr(b, prime));
|
||||
const res = await exec(`${path.join(dir.path, "tester")}` +
|
||||
` ${test[i][0]}` +
|
||||
` ${toMontgomeryStr(a, prime)}` +
|
||||
` ${toMontgomeryStr(b, prime)}`
|
||||
);
|
||||
// console.log(res.stdout);
|
||||
const c=fromMontgomeryStr(res.stdout, prime);
|
||||
|
||||
assert.equal(ec.toString(), c.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
|
||||
|
||||
global <%=name%>_add
|
||||
global <%=name%>_mul
|
||||
global <%=name%>_q
|
||||
DEFAULT REL
|
||||
|
||||
section .text
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; add
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_add:
|
||||
; Add component by component with carry
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rsi + <%=i*8%>]
|
||||
<%= i==0 ? "add" : "adc" %> rax, [rdx + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
jc add_sq ; if overflow, substract q
|
||||
|
||||
; Compare with q
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
<% if (i>0) { %>
|
||||
mov rax, [rdi + <%= (n64-i-1)*8 %>]
|
||||
<% } %>
|
||||
cmp rax, [q + <%= (n64-i-1)*8 %>]
|
||||
jg add_sq
|
||||
jl add_done
|
||||
<% } %>
|
||||
; If equal substract q
|
||||
add_sq:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "sub" : "sbb" %> [rdi + <%=i*8%>], rax
|
||||
mov [rdx + <%=i*8%>], rax
|
||||
<% } %>
|
||||
|
||||
add_done:
|
||||
ret
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul Montgomery
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
mulM:
|
||||
<%
|
||||
let r0, r1, r2;
|
||||
function setR(step) {
|
||||
if ((step % 3) == 0) {
|
||||
r0 = "r8";
|
||||
r1 = "r9";
|
||||
r2 = "r10";
|
||||
} else if ((step % 3) == 1) {
|
||||
r0 = "r9";
|
||||
r1 = "r10";
|
||||
r2 = "r8";
|
||||
} else {
|
||||
r0 = "r10";
|
||||
r1 = "r8";
|
||||
r2 = "r9";
|
||||
}
|
||||
}
|
||||
|
||||
const base = bigInt.one.shiftLeft(64);
|
||||
const np64 = base.minus(q.modInv(base));
|
||||
%>
|
||||
sub rsp, <%= n64*8 %> ; Reserve space for ms
|
||||
mov rcx, rdx ; rdx is needed for multiplications so keep it in cx
|
||||
mov r11, 0x<%= np64.toString(16) %> ; np
|
||||
xor r8,r8
|
||||
xor r9,r9
|
||||
xor r10,r10
|
||||
<%
|
||||
// Main loop
|
||||
for (let i=0; i<n64*2; i++) {
|
||||
setR(i);
|
||||
%>
|
||||
<%
|
||||
// Same Digit
|
||||
for (let o1=Math.max(0, i-n64+1); (o1<=i)&&(o1<n64); o1++) {
|
||||
const o2= i-o1;
|
||||
%>
|
||||
mov rax, [rsi + <%= 8*o1 %>]
|
||||
mul qword [rcx + <%= 8*o2 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} // Same digit
|
||||
%>
|
||||
|
||||
|
||||
<%
|
||||
for (let j=i-1; j>=0; j--) { // All ms
|
||||
if (((i-j)<n64)&&(j<n64)) {
|
||||
%>
|
||||
mov rax, [rsp + <%= j*8 %>]
|
||||
mul qword [q + <%= (i-j)*8 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
}
|
||||
} // ms
|
||||
%>
|
||||
|
||||
<%
|
||||
if (i<n64) {
|
||||
%>
|
||||
mov rax, <%= r0 %>
|
||||
mul r11
|
||||
mov [rsp + <%= i*8 %>], rax
|
||||
mul qword [q]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
mov [rdi + <%= (i-n64)*8 %> ], <%= r0 %>
|
||||
xor <%= r0 %>,<%= r0 %>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<%
|
||||
} // Main Loop
|
||||
%>
|
||||
cmp <%= r1 %>, 0x0
|
||||
jne mulM_sq
|
||||
; Compare with q
|
||||
<%
|
||||
for (let i=0; i<n64; i++) {
|
||||
%>
|
||||
mov rax, [rdi + <%= (n64-i-1)*8 %>]
|
||||
cmp rax, [q + <%= (n64-i-1)*8 %>]
|
||||
jg mulM_sq
|
||||
jl mulM_done
|
||||
<%
|
||||
}
|
||||
%>
|
||||
; If equal substract q
|
||||
|
||||
mulM_sq:
|
||||
<%
|
||||
for (let i=0; i<n64; i++) {
|
||||
%>
|
||||
mov rax, [q + <%= i*8 %>]
|
||||
<%= i==0 ? "sub" : "sbb" %> [rdi + <%= i*8 %>], rax
|
||||
mov [rdx + <%= i*8 %>], rax
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
mulM_done:
|
||||
add rsp, <%= n64*8 %> ; recover rsp
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul MontgomeryShort
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
mulSM:
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_mul:
|
||||
mov rax, [rsi]
|
||||
bt rax, 63
|
||||
jc l1
|
||||
mov rcx, [rdx]
|
||||
bt rcx, 63
|
||||
jc s1l2
|
||||
s1s2: ; short first and second
|
||||
mul ecx
|
||||
jc rs2l ; If if doesn't feed in 32 bits convert the result to long
|
||||
|
||||
; The shorts multiplication is done. copy the val to destination and return
|
||||
mov [rdi], rax
|
||||
ret
|
||||
|
||||
rs2l: ; The result in the multiplication doen't feed
|
||||
; we have the result in edx:eax we need to convert it to long
|
||||
shl rdx, 32
|
||||
mov edx, eax ; pack edx:eax to rdx
|
||||
|
||||
xor rax, rax ; Set the format to long
|
||||
bts rax, 63
|
||||
mov [rdi], rax ; move the first digit
|
||||
|
||||
cmp rdx, 0 ; check if redx is negative.
|
||||
jl rs2ln
|
||||
|
||||
; edx is positive.
|
||||
mov [rdi + 8], rdx ; Set the firs digit
|
||||
|
||||
xor rax, rax ; Set the remaining digits to 0
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov [rdi + <%= (i+1)*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
; edx is negative.
|
||||
rs2ln:
|
||||
|
||||
add rdx, [q] ; Set the firs digit
|
||||
mov [rdi + 8], rdx ;
|
||||
|
||||
mov rdx, -1 ; all ones
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rax, rdx ; Add to q
|
||||
adc rax, [q + <%= i*8 %> ]
|
||||
mov [rdi + <%= (i+1)*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
l1:
|
||||
mov rcx, [rdx]
|
||||
bt rcx, 63
|
||||
jc ll
|
||||
|
||||
l1s2:
|
||||
xor rdx, rdx
|
||||
mov edx, ecx
|
||||
bt rax, 62
|
||||
jc lsM
|
||||
jmp lsN
|
||||
|
||||
s1l2:
|
||||
mov rsi, rdx
|
||||
xor rdx, rdx
|
||||
mov edx, eax
|
||||
bt rcx, 62
|
||||
jc lsM
|
||||
jmp lsN
|
||||
|
||||
|
||||
lsN:
|
||||
mov byte [rdi + 3], 0xC0 ; set the result to montgomery
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
call mulSM
|
||||
mov rdx, R3
|
||||
call mulM
|
||||
ret
|
||||
|
||||
lsM:
|
||||
mov byte [rdi + 3], 0x80 ; set the result to long normal
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
call mulSM
|
||||
ret
|
||||
|
||||
|
||||
ll:
|
||||
|
||||
bt rax, 62
|
||||
jc lml
|
||||
bt rcx, 62
|
||||
jc lnlm
|
||||
|
||||
lnln:
|
||||
mov byte [rdi + 3], 0xC0 ; set the result to long montgomery
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
add rdx, 8
|
||||
call mulM
|
||||
mov rdi, rsi
|
||||
mov rdx, R3
|
||||
call mulM
|
||||
ret
|
||||
|
||||
lml:
|
||||
bt rcx, 62
|
||||
jc lmlm
|
||||
|
||||
lnlm:
|
||||
mov byte [rdi + 3], 0x80 ; set the result to long normal
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
add rdx, 8
|
||||
call mulM
|
||||
ret
|
||||
|
||||
lmlm:
|
||||
mov byte [rdi + 3], 0xC0 ; set the result to long montgomery
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
add rdx, 8
|
||||
call mulM
|
||||
ret
|
||||
|
||||
|
||||
section .data
|
||||
<%=name%>_q:
|
||||
dd 0
|
||||
dd 0x80000000
|
||||
q dq <%= constantElement(q) %>
|
||||
R3 dq <%= constantElement(bigInt.one.shiftLeft(n64*64*3).mod(q)) %>
|
||||
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul Montgomery
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
mulM:
|
||||
<%
|
||||
let r0, r1, r2;
|
||||
function setR(step) {
|
||||
if ((step % 3) == 0) {
|
||||
r0 = "r8";
|
||||
r1 = "r9";
|
||||
r2 = "r10";
|
||||
} else if ((step % 3) == 1) {
|
||||
r0 = "r9";
|
||||
r1 = "r10";
|
||||
r2 = "r8";
|
||||
} else {
|
||||
r0 = "r10";
|
||||
r1 = "r8";
|
||||
r2 = "r9";
|
||||
}
|
||||
}
|
||||
|
||||
const base = bigInt.one.shiftLeft(64);
|
||||
const np64 = base.minus(q.modInv(base));
|
||||
%>
|
||||
sub rsp, <%= n64*8 %> ; Reserve space for ms
|
||||
mov rcx, rdx ; rdx is needed for multiplications so keep it in cx
|
||||
mov r11, 0x<%= np64.toString(16) %> ; np
|
||||
xor r8,r8
|
||||
xor r9,r9
|
||||
xor r10,r10
|
||||
<%
|
||||
// Main loop
|
||||
for (let i=0; i<n64*2; i++) {
|
||||
setR(i);
|
||||
%>
|
||||
<%
|
||||
// Same Digit
|
||||
for (let o1=Math.max(0, i-n64+1); (o1<=i)&&(o1<n64); o1++) {
|
||||
const o2= i-o1;
|
||||
%>
|
||||
mov rax, [rsi + <%= 8*o1 %>]
|
||||
mul qword [rcx + <%= 8*o2 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} // Same digit
|
||||
%>
|
||||
|
||||
|
||||
<%
|
||||
for (let j=i-1; j>=0; j--) { // All ms
|
||||
if (((i-j)<n64)&&(j<n64)) {
|
||||
%>
|
||||
mov rax, [rsp + <%= j*8 %>]
|
||||
mul qword [q + <%= (i-j)*8 %>]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
}
|
||||
} // ms
|
||||
%>
|
||||
|
||||
<%
|
||||
if (i<n64) {
|
||||
%>
|
||||
mov rax, <%= r0 %>
|
||||
mul r11
|
||||
mov [rsp + <%= i*8 %>], rax
|
||||
mul qword [q]
|
||||
add <%= r0 %>, rax
|
||||
adc <%= r1 %>, rdx
|
||||
adc <%= r2 %>, 0x0
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
mov [rdi + <%= (i-n64)*8 %> ], <%= r0 %>
|
||||
xor <%= r0 %>,<%= r0 %>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<%
|
||||
} // Main Loop
|
||||
%>
|
||||
cmp <%= r1 %>, 0x0
|
||||
jne mulM_sq
|
||||
; Compare with q
|
||||
<%
|
||||
for (let i=0; i<n64; i++) {
|
||||
%>
|
||||
mov rax, [rdi + <%= (n64-i-1)*8 %>]
|
||||
cmp rax, [q + <%= (n64-i-1)*8 %>]
|
||||
jg mulM_sq
|
||||
jl mulM_done
|
||||
<%
|
||||
}
|
||||
%>
|
||||
; If equal substract q
|
||||
|
||||
mulM_sq:
|
||||
<%
|
||||
for (let i=0; i<n64; i++) {
|
||||
%>
|
||||
mov rax, [q + <%= i*8 %>]
|
||||
<%= i==0 ? "sub" : "sbb" %> [rdi + <%= i*8 %>], rax
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
mulM_done:
|
||||
add rsp, <%= n64*8 %> ; recover rsp
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul MontgomeryShort
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
mulSM:
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; mul
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_mul:
|
||||
mov rax, [rsi]
|
||||
bt rax, 63
|
||||
jc l1
|
||||
mov rcx, [rdx]
|
||||
bt rcx, 63
|
||||
jc s1l2
|
||||
s1s2: ; short first and second
|
||||
mul ecx
|
||||
jc rs2l ; If if doesn't feed in 32 bits convert the result to long
|
||||
|
||||
; The shorts multiplication is done. copy the val to destination and return
|
||||
mov [rdi], rax
|
||||
ret
|
||||
|
||||
rs2l: ; The result in the multiplication doen't feed
|
||||
; we have the result in edx:eax we need to convert it to long
|
||||
shl rdx, 32
|
||||
mov edx, eax ; pack edx:eax to rdx
|
||||
|
||||
xor rax, rax ; Set the format to long
|
||||
bts rax, 63
|
||||
mov [rdi], rax ; move the first digit
|
||||
|
||||
cmp rdx, 0 ; check if redx is negative.
|
||||
jl rs2ln
|
||||
|
||||
; edx is positive.
|
||||
mov [rdi + 8], rdx ; Set the firs digit
|
||||
|
||||
xor rax, rax ; Set the remaining digits to 0
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov [rdi + <%= (i+1)*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
; edx is negative.
|
||||
rs2ln:
|
||||
|
||||
add rdx, [q] ; Set the firs digit
|
||||
mov [rdi + 8], rdx ;
|
||||
|
||||
mov rdx, -1 ; all ones
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rax, rdx ; Add to q
|
||||
adc rax, [q + <%= i*8 %> ]
|
||||
mov [rdi + <%= (i+1)*8 %>], rax
|
||||
<% } %>
|
||||
ret
|
||||
|
||||
l1:
|
||||
mov rcx, [rdx]
|
||||
bt rcx, 63
|
||||
jc ll
|
||||
|
||||
l1s2:
|
||||
xor rdx, rdx
|
||||
mov edx, ecx
|
||||
bt rax, 62
|
||||
jc lsM
|
||||
jmp lsN
|
||||
|
||||
s1l2:
|
||||
mov rsi, rdx
|
||||
xor rdx, rdx
|
||||
mov edx, eax
|
||||
bt rcx, 62
|
||||
jc lsM
|
||||
jmp lsN
|
||||
|
||||
|
||||
lsN:
|
||||
mov byte [rdi + 7], 0xC0 ; set the result to montgomery
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
call mulSM
|
||||
mov rsi, rdi
|
||||
lea rdx, [R3]
|
||||
call mulM
|
||||
ret
|
||||
|
||||
lsM:
|
||||
mov byte [rdi + 7], 0x80 ; set the result to long normal
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
call mulSM
|
||||
ret
|
||||
|
||||
|
||||
ll:
|
||||
|
||||
bt rax, 62
|
||||
jc lml
|
||||
bt rcx, 62
|
||||
jc lnlm
|
||||
|
||||
lnln:
|
||||
mov byte [rdi + 7], 0xC0 ; set the result to long montgomery
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
add rdx, 8
|
||||
call mulM
|
||||
mov rsi, rdi
|
||||
lea rdx, [R3]
|
||||
call mulM
|
||||
ret
|
||||
|
||||
lml:
|
||||
bt rcx, 62
|
||||
jc lmlm
|
||||
|
||||
lnlm:
|
||||
mov byte [rdi + 7], 0x80 ; set the result to long normal
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
add rdx, 8
|
||||
call mulM
|
||||
ret
|
||||
|
||||
lmlm:
|
||||
mov byte [rdi + 7], 0xC0 ; set the result to long montgomery
|
||||
add rsi, 8
|
||||
add rdi, 8
|
||||
add rdx, 8
|
||||
call mulM
|
||||
ret
|
||||
@@ -1,317 +0,0 @@
|
||||
<% function subS1S2() { %>
|
||||
xor rdx, rdx
|
||||
mov edx, eax
|
||||
sub edx, ecx
|
||||
jo sub_manageOverflow ; rsi already is the 64bits result
|
||||
|
||||
mov [rdi], rdx ; not necessary to adjust so just save and return
|
||||
ret
|
||||
|
||||
sub_manageOverflow: ; Do the operation in 64 bits
|
||||
push rsi
|
||||
movsx rsi, eax
|
||||
movsx rdx, ecx
|
||||
sub rsi, rdx
|
||||
call rawCopyS2L
|
||||
pop rsi
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
<% function subL1S2(t) { %>
|
||||
add rsi, 8
|
||||
movsx rdx, ecx
|
||||
add rdi, 8
|
||||
cmp rdx, 0
|
||||
<% const rawSubLabel = global.tmpLabel() %>
|
||||
jns <%= rawSubLabel %>
|
||||
neg rdx
|
||||
call rawAddLS
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<%= rawSubLabel %>:
|
||||
call rawSubLS
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
|
||||
<% function subS1L2(t) { %>
|
||||
cmp eax, 0
|
||||
<% const s1NegLabel = global.tmpLabel() %>
|
||||
js <%= s1NegLabel %>
|
||||
|
||||
; First Operand is positive
|
||||
push rsi
|
||||
add rdi, 8
|
||||
movsx rsi, eax
|
||||
add rdx, 8
|
||||
call rawSubSL
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
ret
|
||||
|
||||
<%= s1NegLabel %>: ; First operand is negative
|
||||
push rsi
|
||||
lea rsi, [rdx + 8]
|
||||
movsx rdx, eax
|
||||
add rdi, 8
|
||||
neg rdx
|
||||
call rawNegLS
|
||||
sub rdi, 8
|
||||
pop rsi
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
|
||||
<% function subL1L2(t) { %>
|
||||
add rdi, 8
|
||||
add rsi, 8
|
||||
add rdx, 8
|
||||
call rawSubLL
|
||||
sub rdi, 8
|
||||
sub rsi, 8
|
||||
ret
|
||||
<% } %>
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; sub
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Substracts two elements of any kind
|
||||
; Params:
|
||||
; rsi <= Pointer to element 1
|
||||
; rdx <= Pointer to element 2
|
||||
; rdi <= Pointer to result
|
||||
; Modified Registers:
|
||||
; r8, r9, 10, r11, rax, rcx
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
<%=name%>_sub:
|
||||
mov rax, [rsi]
|
||||
mov rcx, [rdx]
|
||||
bt rax, 63 ; Check if is long first operand
|
||||
jc sub_l1
|
||||
bt rcx, 63 ; Check if is long second operand
|
||||
jc sub_s1l2
|
||||
|
||||
sub_s1s2: ; Both operands are short
|
||||
<%= subS1S2() %>
|
||||
sub_l1:
|
||||
bt rcx, 63 ; Check if is short second operand
|
||||
jc sub_l1l2
|
||||
|
||||
;;;;;;;;
|
||||
sub_l1s2:
|
||||
bt rax, 62 ; check if montgomery first
|
||||
jc sub_l1ms2
|
||||
sub_l1ns2:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= subL1S2(); %>
|
||||
|
||||
sub_l1ms2:
|
||||
bt rcx, 62 ; check if montgomery second
|
||||
jc sub_l1ms2m
|
||||
sub_l1ms2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_b() %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
sub_l1ms2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
|
||||
;;;;;;;;
|
||||
sub_s1l2:
|
||||
bt rcx, 62 ; check if montgomery first
|
||||
jc sub_s1l2m
|
||||
sub_s1l2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= subS1L2(); %>
|
||||
|
||||
sub_s1l2m:
|
||||
bt rax, 62 ; check if montgomery second
|
||||
jc sub_s1ml2m
|
||||
sub_s1nl2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_a() %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
sub_s1ml2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
;;;;
|
||||
sub_l1l2:
|
||||
bt rax, 62 ; check if montgomery first
|
||||
jc sub_l1ml2
|
||||
sub_l1nl2:
|
||||
bt rcx, 62 ; check if montgomery second
|
||||
jc sub_l1nl2m
|
||||
sub_l1nl2n:
|
||||
<%= global.setTypeDest("0x80"); %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
sub_l1nl2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_a(); %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
sub_l1ml2:
|
||||
bt rcx, 62 ; check if montgomery seconf
|
||||
jc sub_l1ml2m
|
||||
sub_l1ml2n:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= global.toMont_b(); %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
sub_l1ml2m:
|
||||
<%= global.setTypeDest("0xC0"); %>
|
||||
<%= subL1L2() %>
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawSubLS
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Substracts a short element from the long element
|
||||
; Params:
|
||||
; rdi <= Pointer to the long data of result
|
||||
; rsi <= Pointer to the long data of element 1 where will be substracted
|
||||
; rdx <= Value to be substracted
|
||||
; [rdi] = [rsi] - rdx
|
||||
; Modified Registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawSubLS:
|
||||
; Substract first digit
|
||||
|
||||
mov rax, [rsi]
|
||||
sub rax, rdx
|
||||
mov [rdi] ,rax
|
||||
mov rdx, 0
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rax, [rsi + <%=i*8%>]
|
||||
sbb rax, rdx
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
jnc rawSubLS_done ; if overflow, add q
|
||||
|
||||
; Add q
|
||||
rawSubLS_aq:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "add" : "adc" %> [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
rawSubLS_done:
|
||||
ret
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawSubSL
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Substracts a long element from a short element
|
||||
; Params:
|
||||
; rdi <= Pointer to the long data of result
|
||||
; rsi <= Value from where will bo substracted
|
||||
; rdx <= Pointer to long of the value to be substracted
|
||||
;
|
||||
; [rdi] = rsi - [rdx]
|
||||
; Modified Registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawSubSL:
|
||||
; Substract first digit
|
||||
sub rsi, [rdx]
|
||||
mov [rdi] ,rsi
|
||||
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rax, 0
|
||||
sbb rax, [rdx + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
jnc rawSubSL_done ; if overflow, add q
|
||||
|
||||
; Add q
|
||||
rawSubSL_aq:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "add" : "adc" %> [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
rawSubSL_done:
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawSubLL
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Substracts a long element from a short element
|
||||
; Params:
|
||||
; rdi <= Pointer to the long data of result
|
||||
; rsi <= Pointer to long from where substracted
|
||||
; rdx <= Pointer to long of the value to be substracted
|
||||
;
|
||||
; [rdi] = [rsi] - [rdx]
|
||||
; Modified Registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawSubLL:
|
||||
; Substract first digit
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rsi + <%=i*8%>]
|
||||
<%= i==0 ? "sub" : "sbb" %> rax, [rdx + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
jnc rawSubLL_done ; if overflow, add q
|
||||
|
||||
; Add q
|
||||
rawSubLL_aq:
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "add" : "adc" %> [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
rawSubLL_done:
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; rawNegLS
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Substracts a long element and a short element form 0
|
||||
; Params:
|
||||
; rdi <= Pointer to the long data of result
|
||||
; rsi <= Pointer to long from where substracted
|
||||
; rdx <= short value to be substracted too
|
||||
;
|
||||
; [rdi] = -[rsi] - rdx
|
||||
; Modified Registers:
|
||||
; rax
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
rawNegLS:
|
||||
mov rax, [q]
|
||||
sub rax, rdx
|
||||
mov [rdi], rax
|
||||
<% for (let i=1; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%> ]
|
||||
sbb rax, 0
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
setc dl
|
||||
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [rdi + <%=i*8%> ]
|
||||
<%= i==0 ? "sub" : "sbb" %> rax, [rsi + <%=i*8%>]
|
||||
mov [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
|
||||
setc dh
|
||||
or dl, dh
|
||||
jz rawNegSL_done
|
||||
|
||||
; it is a negative value, so add q
|
||||
<% for (let i=0; i<n64; i++) { %>
|
||||
mov rax, [q + <%=i*8%>]
|
||||
<%= i==0 ? "add" : "adc" %> [rdi + <%=i*8%>], rax
|
||||
<% } %>
|
||||
|
||||
rawNegSL_done:
|
||||
ret
|
||||
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdio.h> /* printf, NULL */
|
||||
#include <stdlib.h>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
#include "fr.h"
|
||||
|
||||
|
||||
typedef void (*Func1)(PFrElement, PFrElement);
|
||||
typedef void (*Func2)(PFrElement, PFrElement, PFrElement);
|
||||
typedef void *FuncAny;
|
||||
|
||||
typedef struct {
|
||||
FuncAny fn;
|
||||
int nOps;
|
||||
} FunctionSpec;
|
||||
|
||||
std::map<std::string, FunctionSpec> functions;
|
||||
std::vector<FrElement> stack;
|
||||
|
||||
void addFunction(std::string name, FuncAny f, int nOps) {
|
||||
FunctionSpec fs;
|
||||
fs.fn = f;
|
||||
fs.nOps = nOps;
|
||||
functions[name] = fs;
|
||||
}
|
||||
|
||||
void fillMap() {
|
||||
addFunction("add", (FuncAny)Fr_add, 2);
|
||||
addFunction("sub", (FuncAny)Fr_sub, 2);
|
||||
addFunction("neg", (FuncAny)Fr_neg, 1);
|
||||
addFunction("mul", (FuncAny)Fr_mul, 2);
|
||||
addFunction("square", (FuncAny)Fr_square, 1);
|
||||
addFunction("idiv", (FuncAny)Fr_idiv, 2);
|
||||
addFunction("inv", (FuncAny)Fr_inv, 1);
|
||||
addFunction("div", (FuncAny)Fr_div, 2);
|
||||
addFunction("band", (FuncAny)Fr_band, 2);
|
||||
addFunction("bor", (FuncAny)Fr_bor, 2);
|
||||
addFunction("bxor", (FuncAny)Fr_bxor, 2);
|
||||
addFunction("bnot", (FuncAny)Fr_bnot, 1);
|
||||
addFunction("eq", (FuncAny)Fr_eq, 2);
|
||||
addFunction("neq", (FuncAny)Fr_neq, 2);
|
||||
addFunction("lt", (FuncAny)Fr_lt, 2);
|
||||
addFunction("gt", (FuncAny)Fr_gt, 2);
|
||||
addFunction("leq", (FuncAny)Fr_leq, 2);
|
||||
addFunction("geq", (FuncAny)Fr_geq, 2);
|
||||
addFunction("land", (FuncAny)Fr_land, 2);
|
||||
addFunction("lor", (FuncAny)Fr_lor, 2);
|
||||
addFunction("lnot", (FuncAny)Fr_lnot, 1);
|
||||
}
|
||||
|
||||
u_int64_t readInt(std::string &s) {
|
||||
if (s.rfind("0x", 0) == 0) {
|
||||
return std::stoull(s.substr(2), 0, 16);
|
||||
} else {
|
||||
return std::stoull(s, 0, 10);
|
||||
}
|
||||
}
|
||||
|
||||
void pushNumber(std::vector<std::string> &v) {
|
||||
u_int64_t a;
|
||||
if ((v.size()<1) || (v.size() > (Fr_N64+1))) {
|
||||
printf("Invalid Size: %d - %d \n", v.size(), Fr_N64);
|
||||
throw std::runtime_error("Invalid number of parameters for number");
|
||||
}
|
||||
FrElement e;
|
||||
a = readInt(v[0]);
|
||||
*(u_int64_t *)(&e) = a;
|
||||
for (int i=0; i<Fr_N64; i++) {
|
||||
if (i+1 < v.size()) {
|
||||
a = readInt(v[i+1]);
|
||||
} else {
|
||||
a = 0;
|
||||
}
|
||||
e.longVal[i] = a;
|
||||
}
|
||||
stack.push_back(e);
|
||||
}
|
||||
|
||||
void callFunction(FunctionSpec fs) {
|
||||
if (stack.size() < fs.nOps) {
|
||||
throw new std::runtime_error("Not enough elements in stack");
|
||||
}
|
||||
if (fs.nOps == 1) {
|
||||
FrElement a = stack.back();
|
||||
stack.pop_back();
|
||||
FrElement c;
|
||||
(*(Func1)fs.fn)(&c, &a);
|
||||
stack.push_back(c);
|
||||
} else if (fs.nOps == 2) {
|
||||
FrElement b = stack.back();
|
||||
stack.pop_back();
|
||||
FrElement a = stack.back();
|
||||
stack.pop_back();
|
||||
FrElement c;
|
||||
(*(Func2)fs.fn)(&c, &a, &b);
|
||||
stack.push_back(c);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void processLine(std::string &line) {
|
||||
std::regex re("(\\s*[,;]\\s*)|\\s+"); // whitespace
|
||||
|
||||
std::sregex_token_iterator begin( line.begin(), line.end(), re ,-1);
|
||||
std::sregex_token_iterator end;
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
std::copy(begin, end, std::back_inserter(tokens));
|
||||
|
||||
// Remove initial empty tokens
|
||||
while ((tokens.size() > 0)&&(tokens[0] == "")) {
|
||||
tokens.erase(tokens.begin());
|
||||
}
|
||||
|
||||
// Empty lines are valid but are not processed
|
||||
if (tokens.size() == 0) return;
|
||||
|
||||
auto search = functions.find(tokens[0]);
|
||||
if (search == functions.end()) {
|
||||
pushNumber(tokens);
|
||||
} else {
|
||||
if (tokens.size() != 1) {
|
||||
throw std::runtime_error("Functions does not accept parameters");
|
||||
}
|
||||
callFunction(search->second);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Fr_init();
|
||||
fillMap();
|
||||
std::string line;
|
||||
int i=0;
|
||||
while (std::getline(std::cin, line)) {
|
||||
processLine(line);
|
||||
// if (i%1000 == 0) printf("%d\n", i);
|
||||
// printf("%d\n", i);
|
||||
i++;
|
||||
}
|
||||
// Print the elements in the stack
|
||||
//
|
||||
for (int i=0; i<stack.size(); i++) {
|
||||
char *s;
|
||||
s = Fr_element2str(&stack[i]);
|
||||
printf("%s\n", s);
|
||||
free(s);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "fr.h"
|
||||
|
||||
typedef void (*Func2)(PFrElement, PFrElement, PFrElement);
|
||||
|
||||
typedef struct {
|
||||
const char *fnName;
|
||||
Func2 fn;
|
||||
} FN;
|
||||
|
||||
|
||||
#define NFN 2
|
||||
FN fns[NFN] = {
|
||||
{"add", Fr_add},
|
||||
{"mul", Fr_mul},
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc <= 1) {
|
||||
fprintf( stderr, "invalid number of parameters");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i=0; i< NFN;i++) {
|
||||
if (strcmp(argv[1], fns[i].fnName) == 0) {
|
||||
if (argc != 4) {
|
||||
fprintf( stderr, "invalid number of parameters");
|
||||
return 1;
|
||||
}
|
||||
FrElement a;
|
||||
FrElement b;
|
||||
|
||||
Fr_str2element(&a, argv[2]);
|
||||
Fr_str2element(&b, argv[3]);
|
||||
FrElement c;
|
||||
fns[i].fn(&c, &a, &b);
|
||||
|
||||
char *s;
|
||||
s = Fr_element2str(&c);
|
||||
printf("%s", s);
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fprintf( stderr, "invalid operation %s", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -1,73 +0,0 @@
|
||||
<% global.setTypeDest = function (t) {
|
||||
return (
|
||||
` mov r11b, ${t}
|
||||
shl r11, 56
|
||||
mov [rdi], r11`);
|
||||
} %>
|
||||
|
||||
|
||||
<% global.toMont_a = function () {
|
||||
return (
|
||||
` push rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
call ${name}_toMontgomery
|
||||
mov rdx, rsi
|
||||
mov rsi, rdi
|
||||
pop rdi`);
|
||||
} %>
|
||||
|
||||
<% global.toMont_b = function() {
|
||||
return (
|
||||
` push rdi
|
||||
mov rdi, rdx
|
||||
call ${name}_toMontgomery
|
||||
mov rdx, rdi
|
||||
pop rdi`);
|
||||
} %>
|
||||
|
||||
<% global.fromMont_a = function () {
|
||||
return (
|
||||
` push rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
call ${name}_toNormal
|
||||
mov rdx, rsi
|
||||
mov rsi, rdi
|
||||
pop rdi`);
|
||||
} %>
|
||||
|
||||
<% global.fromMont_b = function() {
|
||||
return (
|
||||
` push rdi
|
||||
mov rdi, rdx
|
||||
call ${name}_toNormal
|
||||
mov rdx, rdi
|
||||
pop rdi`);
|
||||
} %>
|
||||
|
||||
<% global.toLong_a = function () {
|
||||
return (
|
||||
` push rdi
|
||||
push rdx
|
||||
mov rdi, rsi
|
||||
movsx rsi, r8d
|
||||
call rawCopyS2L
|
||||
mov rsi, rdi
|
||||
pop rdx
|
||||
pop rdi`);
|
||||
} %>
|
||||
|
||||
<% global.toLong_b = function() {
|
||||
return (
|
||||
` push rdi
|
||||
push rsi
|
||||
mov rdi, rdx
|
||||
movsx rsi, r9d
|
||||
call rawCopyS2L
|
||||
mov rdx, rdi
|
||||
pop rsi
|
||||
pop rdi`);
|
||||
} %>
|
||||
|
||||
|
||||
229
c/calcwit.cpp
229
c/calcwit.cpp
@@ -1,229 +0,0 @@
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <thread>
|
||||
#include "calcwit.h"
|
||||
#include "utils.h"
|
||||
|
||||
Circom_CalcWit::Circom_CalcWit(Circom_Circuit *aCircuit) {
|
||||
circuit = aCircuit;
|
||||
|
||||
#ifdef SANITY_CHECK
|
||||
signalAssigned = new bool[circuit->NSignals];
|
||||
signalAssigned[0] = true;
|
||||
#endif
|
||||
|
||||
mutexes = new std::mutex[NMUTEXES];
|
||||
cvs = new std::condition_variable[NMUTEXES];
|
||||
inputSignalsToTrigger = new int[circuit->NComponents];
|
||||
signalValues = new FrElement[circuit->NSignals];
|
||||
|
||||
// Set one signal
|
||||
Fr_copy(&signalValues[0], circuit->constants + 1);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
Circom_CalcWit::~Circom_CalcWit() {
|
||||
|
||||
#ifdef SANITY_CHECK
|
||||
delete signalAssigned;
|
||||
#endif
|
||||
|
||||
delete[] cvs;
|
||||
delete[] mutexes;
|
||||
|
||||
delete[] signalValues;
|
||||
delete[] inputSignalsToTrigger;
|
||||
|
||||
}
|
||||
|
||||
void Circom_CalcWit::syncPrintf(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
printf_mutex.lock();
|
||||
vprintf(format, args);
|
||||
printf_mutex.unlock();
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Circom_CalcWit::reset() {
|
||||
|
||||
#ifdef SANITY_CHECK
|
||||
for (int i=1; i<circuit->NComponents; i++) signalAssigned[i] = false;
|
||||
#endif
|
||||
|
||||
for (int i=0; i<circuit->NComponents; i++) {
|
||||
inputSignalsToTrigger[i] = circuit->components[i].inputSignals;
|
||||
if (inputSignalsToTrigger[i] == 0) triggerComponent(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Circom_CalcWit::getSubComponentOffset(int cIdx, u64 hash) {
|
||||
int hIdx;
|
||||
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) {
|
||||
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash));
|
||||
}
|
||||
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos;
|
||||
if (circuit->components[cIdx].entries[entryPos].type != _typeComponent) {
|
||||
throw std::runtime_error("invalid type");
|
||||
}
|
||||
return circuit->components[cIdx].entries[entryPos].offset;
|
||||
}
|
||||
|
||||
|
||||
Circom_Sizes Circom_CalcWit::getSubComponentSizes(int cIdx, u64 hash) {
|
||||
int hIdx;
|
||||
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) {
|
||||
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash));
|
||||
}
|
||||
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos;
|
||||
if (circuit->components[cIdx].entries[entryPos].type != _typeComponent) {
|
||||
throw std::runtime_error("invalid type");
|
||||
}
|
||||
return circuit->components[cIdx].entries[entryPos].sizes;
|
||||
}
|
||||
|
||||
int Circom_CalcWit::getSignalOffset(int cIdx, u64 hash) {
|
||||
int hIdx;
|
||||
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) {
|
||||
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash));
|
||||
}
|
||||
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos;
|
||||
if (circuit->components[cIdx].entries[entryPos].type != _typeSignal) {
|
||||
throw std::runtime_error("invalid type");
|
||||
}
|
||||
return circuit->components[cIdx].entries[entryPos].offset;
|
||||
}
|
||||
|
||||
Circom_Sizes Circom_CalcWit::getSignalSizes(int cIdx, u64 hash) {
|
||||
int hIdx;
|
||||
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) {
|
||||
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash));
|
||||
}
|
||||
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos;
|
||||
if (circuit->components[cIdx].entries[entryPos].type != _typeSignal) {
|
||||
throw std::runtime_error("invalid type");
|
||||
}
|
||||
return circuit->components[cIdx].entries[entryPos].sizes;
|
||||
}
|
||||
|
||||
void Circom_CalcWit::getSignal(int currentComponentIdx, int cIdx, int sIdx, PFrElement value) {
|
||||
// syncPrintf("getSignal: %d\n", sIdx);
|
||||
if ((circuit->components[cIdx].newThread)&&(currentComponentIdx != cIdx)) {
|
||||
std::unique_lock<std::mutex> lk(mutexes[cIdx % NMUTEXES]);
|
||||
while (inputSignalsToTrigger[cIdx] != -1) {
|
||||
cvs[cIdx % NMUTEXES].wait(lk);
|
||||
}
|
||||
// cvs[cIdx % NMUTEXES].wait(lk, [&]{return inputSignalsToTrigger[cIdx] == -1;});
|
||||
lk.unlock();
|
||||
}
|
||||
#ifdef SANITY_CHECK
|
||||
if (signalAssigned[sIdx] == false) {
|
||||
fprintf(stderr, "Accessing a not assigned signal: %d\n", sIdx);
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
Fr_copy(value, signalValues + sIdx);
|
||||
/*
|
||||
char *valueStr = mpz_get_str(0, 10, *value);
|
||||
syncPrintf("%d, Get %d --> %s\n", currentComponentIdx, sIdx, valueStr);
|
||||
free(valueStr);
|
||||
*/
|
||||
}
|
||||
|
||||
void Circom_CalcWit::finished(int cIdx) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mutexes[cIdx % NMUTEXES]);
|
||||
inputSignalsToTrigger[cIdx] = -1;
|
||||
}
|
||||
// syncPrintf("Finished: %d\n", cIdx);
|
||||
cvs[cIdx % NMUTEXES].notify_all();
|
||||
}
|
||||
|
||||
void Circom_CalcWit::setSignal(int currentComponentIdx, int cIdx, int sIdx, PFrElement value) {
|
||||
// syncPrintf("setSignal: %d\n", sIdx);
|
||||
|
||||
#ifdef SANITY_CHECK
|
||||
if (signalAssigned[sIdx] == true) {
|
||||
fprintf(stderr, "Signal assigned twice: %d\n", sIdx);
|
||||
assert(false);
|
||||
}
|
||||
signalAssigned[sIdx] = true;
|
||||
#endif
|
||||
// Log assignement
|
||||
/*
|
||||
char *valueStr = mpz_get_str(0, 10, *value);
|
||||
syncPrintf("%d, Set %d --> %s\n", currentComponentIdx, sIdx, valueStr);
|
||||
free(valueStr);
|
||||
*/
|
||||
Fr_copy(signalValues + sIdx, value);
|
||||
if ( BITMAP_ISSET(circuit->mapIsInput, sIdx) ) {
|
||||
if (inputSignalsToTrigger[cIdx]>0) {
|
||||
inputSignalsToTrigger[cIdx]--;
|
||||
if (inputSignalsToTrigger[cIdx] == 0) triggerComponent(cIdx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Circom_CalcWit::checkConstraint(int currentComponentIdx, PFrElement value1, PFrElement value2, char const *err) {
|
||||
#ifdef SANITY_CHECK
|
||||
FrElement tmp;
|
||||
Fr_eq(&tmp, value1, value2);
|
||||
if (!Fr_isTrue(&tmp)) {
|
||||
char *pcV1 = Fr_element2str(value1);
|
||||
char *pcV2 = Fr_element2str(value2);
|
||||
// throw std::runtime_error(std::to_string(currentComponentIdx) + std::string(", Constraint doesn't match, ") + err + ". " + sV1 + " != " + sV2 );
|
||||
fprintf(stderr, "Constraint doesn't match, %s: %s != %s", err, pcV1, pcV2);
|
||||
free(pcV1);
|
||||
free(pcV2);
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Circom_CalcWit::triggerComponent(int newCIdx) {
|
||||
//int oldCIdx = cIdx;
|
||||
// cIdx = newCIdx;
|
||||
if (circuit->components[newCIdx].newThread) {
|
||||
// syncPrintf("Triggered: %d\n", newCIdx);
|
||||
std::thread t(circuit->components[newCIdx].fn, this, newCIdx);
|
||||
// t.join();
|
||||
t.detach();
|
||||
} else {
|
||||
(*(circuit->components[newCIdx].fn))(this, newCIdx);
|
||||
}
|
||||
// cIdx = oldCIdx;
|
||||
}
|
||||
|
||||
void Circom_CalcWit::log(PFrElement value) {
|
||||
char *pcV = Fr_element2str(value);
|
||||
syncPrintf("Log: %s\n", pcV);
|
||||
free(pcV);
|
||||
}
|
||||
|
||||
void Circom_CalcWit::join() {
|
||||
for (int i=0; i<circuit->NComponents; i++) {
|
||||
std::unique_lock<std::mutex> lk(mutexes[i % NMUTEXES]);
|
||||
while (inputSignalsToTrigger[i] != -1) {
|
||||
cvs[i % NMUTEXES].wait(lk);
|
||||
}
|
||||
// cvs[i % NMUTEXES].wait(lk, [&]{return inputSignalsToTrigger[i] == -1;});
|
||||
lk.unlock();
|
||||
// syncPrintf("Joined: %d\n", i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
73
c/calcwit.h
73
c/calcwit.h
@@ -1,73 +0,0 @@
|
||||
#ifndef CIRCOM_CALCWIT_H
|
||||
#define CIRCOM_CALCWIT_H
|
||||
|
||||
#include "circom.h"
|
||||
#include "fr.h"
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#define NMUTEXES 128
|
||||
|
||||
class Circom_CalcWit {
|
||||
|
||||
#ifdef SANITY_CHECK
|
||||
bool *signalAssigned;
|
||||
#endif
|
||||
|
||||
// componentStatus -> For each component
|
||||
// >0 Signals required to trigger
|
||||
// == 0 Component triggered
|
||||
// == -1 Component finished
|
||||
int *inputSignalsToTrigger;
|
||||
std::mutex *mutexes;
|
||||
std::condition_variable *cvs;
|
||||
|
||||
std::mutex printf_mutex;
|
||||
|
||||
FrElement *signalValues;
|
||||
|
||||
|
||||
void triggerComponent(int newCIdx);
|
||||
void calculateWitness(void *input, void *output);
|
||||
|
||||
void syncPrintf(const char *format, ...);
|
||||
|
||||
|
||||
public:
|
||||
Circom_Circuit *circuit;
|
||||
|
||||
// Functions called by the circuit
|
||||
Circom_CalcWit(Circom_Circuit *aCircuit);
|
||||
~Circom_CalcWit();
|
||||
|
||||
int getSubComponentOffset(int cIdx, u64 hash);
|
||||
Circom_Sizes getSubComponentSizes(int cIdx, u64 hash);
|
||||
int getSignalOffset(int cIdx, u64 hash);
|
||||
Circom_Sizes getSignalSizes(int cIdx, u64 hash);
|
||||
|
||||
void getSignal(int currentComponentIdx, int cIdx, int sIdx, PFrElement value);
|
||||
void setSignal(int currentComponentIdx, int cIdx, int sIdx, PFrElement value);
|
||||
|
||||
void checkConstraint(int currentComponentIdx, PFrElement value1, PFrElement value2, char const *err);
|
||||
|
||||
void log(PFrElement value);
|
||||
|
||||
void finished(int cIdx);
|
||||
void join();
|
||||
|
||||
|
||||
// Public functions
|
||||
inline void setInput(int idx, PFrElement val) {
|
||||
setSignal(0, 0, circuit->wit2sig[idx], val);
|
||||
}
|
||||
inline void getWitness(int idx, PFrElement val) {
|
||||
Fr_copy(val, &signalValues[circuit->wit2sig[idx]]);
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // CIRCOM_CALCWIT_H
|
||||
58
c/circom.h
58
c/circom.h
@@ -1,58 +0,0 @@
|
||||
#ifndef __CIRCOM_H
|
||||
#define __CIRCOM_H
|
||||
|
||||
#include <gmp.h>
|
||||
#include <stdint.h>
|
||||
#include "fr.h"
|
||||
|
||||
class Circom_CalcWit;
|
||||
typedef unsigned long long u64;
|
||||
typedef uint32_t u32;
|
||||
typedef uint8_t u8;
|
||||
|
||||
typedef int Circom_Size;
|
||||
typedef Circom_Size *Circom_Sizes;
|
||||
|
||||
struct Circom_HashEntry {
|
||||
u64 hash;
|
||||
int pos;
|
||||
};
|
||||
typedef Circom_HashEntry *Circom_HashTable;
|
||||
|
||||
typedef enum { _typeSignal, _typeComponent} Circom_EntryType;
|
||||
|
||||
struct Circom_ComponentEntry {
|
||||
int offset;
|
||||
Circom_Sizes sizes;
|
||||
Circom_EntryType type;
|
||||
};
|
||||
typedef Circom_ComponentEntry *Circom_ComponentEntries;
|
||||
|
||||
typedef void (*Circom_ComponentFunction)(Circom_CalcWit *ctx, int __cIdx);
|
||||
|
||||
struct Circom_Component {
|
||||
Circom_HashTable hashTable;
|
||||
Circom_ComponentEntries entries;
|
||||
Circom_ComponentFunction fn;
|
||||
int inputSignals;
|
||||
bool newThread;
|
||||
};
|
||||
|
||||
class Circom_Circuit {
|
||||
public:
|
||||
int NSignals;
|
||||
int NComponents;
|
||||
int NInputs;
|
||||
int NOutputs;
|
||||
int NVars;
|
||||
int *wit2sig;
|
||||
Circom_Component *components;
|
||||
u32 *mapIsInput;
|
||||
PFrElement constants;
|
||||
const char *P;
|
||||
};
|
||||
|
||||
#define BITMAP_ISSET(m, b) (m[b>>5] & (1 << (b&0x1F)))
|
||||
extern struct Circom_Circuit _circuit;
|
||||
|
||||
#endif
|
||||
202
c/main.cpp
202
c/main.cpp
@@ -1,202 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
#include "calcwit.h"
|
||||
#include "circom.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define handle_error(msg) \
|
||||
do { perror(msg); exit(EXIT_FAILURE); } while (0)
|
||||
|
||||
void loadBin(Circom_CalcWit *ctx, std::string filename) {
|
||||
int fd;
|
||||
struct stat sb;
|
||||
|
||||
// map input
|
||||
fd = open(filename.c_str(), O_RDONLY);
|
||||
if (fd == -1)
|
||||
handle_error("open");
|
||||
|
||||
if (fstat(fd, &sb) == -1) /* To obtain file size */
|
||||
handle_error("fstat");
|
||||
|
||||
|
||||
u8 *in;
|
||||
|
||||
in = (u8 *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (in == MAP_FAILED)
|
||||
handle_error("mmap");
|
||||
|
||||
close(fd);
|
||||
|
||||
FrElement v;
|
||||
u8 *p = in;
|
||||
for (int i=0; i<_circuit.NInputs; i++) {
|
||||
v.type = Fr_LONG;
|
||||
for (int j=0; j<Fr_N64; j++) {
|
||||
v.longVal[j] = *(u64 *)p;
|
||||
}
|
||||
p += 8;
|
||||
ctx->setSignal(0, 0, _circuit.wit2sig[1 + _circuit.NOutputs + i], &v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef void (*ItFunc)(Circom_CalcWit *ctx, int idx, json val);
|
||||
|
||||
void iterateArr(Circom_CalcWit *ctx, int o, Circom_Sizes sizes, json jarr, ItFunc f) {
|
||||
if (!jarr.is_array()) {
|
||||
assert((sizes[0] == 1)&&(sizes[1] == 0));
|
||||
f(ctx, o, jarr);
|
||||
} else {
|
||||
int n = sizes[0] / sizes[1];
|
||||
for (int i=0; i<n; i++) {
|
||||
iterateArr(ctx, o + i*sizes[1], sizes+1, jarr[i], f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void itFunc(Circom_CalcWit *ctx, int o, json val) {
|
||||
|
||||
FrElement v;
|
||||
|
||||
std::string s;
|
||||
|
||||
if (val.is_string()) {
|
||||
s = val.get<std::string>();
|
||||
} else if (val.is_number()) {
|
||||
|
||||
double vd = val.get<double>();
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(0) << vd;
|
||||
s = stream.str();
|
||||
} else {
|
||||
handle_error("Invalid JSON type");
|
||||
}
|
||||
|
||||
Fr_str2element (&v, s.c_str());
|
||||
|
||||
ctx->setSignal(0, 0, o, &v);
|
||||
}
|
||||
|
||||
|
||||
void loadJson(Circom_CalcWit *ctx, std::string filename) {
|
||||
std::ifstream inStream(filename);
|
||||
json j;
|
||||
inStream >> j;
|
||||
|
||||
for (json::iterator it = j.begin(); it != j.end(); ++it) {
|
||||
// std::cout << it.key() << " => " << it.value() << '\n';
|
||||
u64 h = fnv1a(it.key());
|
||||
int o;
|
||||
try {
|
||||
o = ctx->getSignalOffset(0, h);
|
||||
} catch (std::runtime_error e) {
|
||||
std::ostringstream errStrStream;
|
||||
errStrStream << "Error loadin variable: " << it.key() << "\n" << e.what();
|
||||
throw std::runtime_error(errStrStream.str() );
|
||||
}
|
||||
Circom_Sizes sizes = ctx->getSignalSizes(0, h);
|
||||
iterateArr(ctx, o, sizes, it.value(), itFunc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void writeOutBin(Circom_CalcWit *ctx, std::string filename) {
|
||||
FILE *write_ptr;
|
||||
|
||||
write_ptr = fopen(filename.c_str(),"wb");
|
||||
|
||||
FrElement v;
|
||||
|
||||
u8 buffOut[256];
|
||||
for (int i=0;i<_circuit.NVars;i++) {
|
||||
size_t size=256;
|
||||
ctx->getWitness(i, &v);
|
||||
Fr_toLongNormal(&v);
|
||||
fwrite(v.longVal, Fr_N64*8, 1, write_ptr);
|
||||
}
|
||||
fclose(write_ptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void writeOutJson(Circom_CalcWit *ctx, std::string filename) {
|
||||
|
||||
std::ofstream outFile;
|
||||
outFile.open (filename);
|
||||
|
||||
outFile << "[\n";
|
||||
|
||||
FrElement v;
|
||||
|
||||
for (int i=0;i<_circuit.NVars;i++) {
|
||||
ctx->getWitness(i, &v);
|
||||
char *pcV = Fr_element2str(&v);
|
||||
std::string sV = std::string(pcV);
|
||||
outFile << (i ? "," : " ") << "\"" << sV << "\"\n";
|
||||
free(pcV);
|
||||
}
|
||||
|
||||
outFile << "]\n";
|
||||
outFile.close();
|
||||
}
|
||||
|
||||
bool hasEnding (std::string const &fullString, std::string const &ending) {
|
||||
if (fullString.length() >= ending.length()) {
|
||||
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Fr_init();
|
||||
if (argc!=3) {
|
||||
std::string cl = argv[0];
|
||||
std::string base_filename = cl.substr(cl.find_last_of("/\\") + 1);
|
||||
std::cout << "Usage: " << base_filename << " <input.<bin|json>> <output.<bin|json>>\n";
|
||||
} else {
|
||||
|
||||
// open output
|
||||
Circom_CalcWit *ctx = new Circom_CalcWit(&_circuit);
|
||||
|
||||
std::string infilename = argv[1];
|
||||
|
||||
if (hasEnding(infilename, std::string(".bin"))) {
|
||||
loadBin(ctx, infilename);
|
||||
} else if (hasEnding(infilename, std::string(".json"))) {
|
||||
loadJson(ctx, infilename);
|
||||
} else {
|
||||
handle_error("Invalid input extension (.bin / .json)");
|
||||
}
|
||||
|
||||
ctx->join();
|
||||
|
||||
printf("Finished!\n");
|
||||
|
||||
std::string outfilename = argv[2];
|
||||
|
||||
if (hasEnding(outfilename, std::string(".bin"))) {
|
||||
writeOutBin(ctx, outfilename);
|
||||
} else if (hasEnding(outfilename, std::string(".json"))) {
|
||||
writeOutJson(ctx, outfilename);
|
||||
} else {
|
||||
handle_error("Invalid output extension (.bin / .json)");
|
||||
}
|
||||
|
||||
delete ctx;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
BIN
c/mainjson
BIN
c/mainjson
Binary file not shown.
@@ -1,47 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
#include "utils.h"
|
||||
#include "circom.h"
|
||||
#include "calcwit.h"
|
||||
|
||||
auto j = R"(
|
||||
{
|
||||
"in": "314"
|
||||
}
|
||||
)"_json;
|
||||
|
||||
typedef void (*ItFunc)(int idx, json val);
|
||||
|
||||
void iterateArr(int o, Circom_Sizes sizes, json jarr, ItFunc f) {
|
||||
if (!jarr.is_array()) {
|
||||
assert((sizes[0] == 1)&&(sizes[1] == 0));
|
||||
f(o, jarr);
|
||||
} else {
|
||||
int n = sizes[0] / sizes[1];
|
||||
for (int i=0; i<n; i++) {
|
||||
iterateArr(o + i*sizes[1], sizes+1, jarr[i], f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void itFunc(int o, json v) {
|
||||
std::cout << o << " <-- " << v << '\n';
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
Circom_CalcWit *ctx = new Circom_CalcWit(&_circuit);
|
||||
|
||||
for (json::iterator it = j.begin(); it != j.end(); ++it) {
|
||||
// std::cout << it.key() << " => " << it.value() << '\n';
|
||||
u64 h = fnv1a(it.key());
|
||||
int o = ctx->getSignalOffset(0, h);
|
||||
Circom_Sizes sizes = ctx->getSignalSizes(0, h);
|
||||
iterateArr(o, sizes, it.value(), itFunc);
|
||||
}
|
||||
}
|
||||
|
||||
25
c/utils.cpp
25
c/utils.cpp
@@ -1,25 +0,0 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
std::string int_to_hex( u64 i )
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << "0x"
|
||||
<< std::setfill ('0') << std::setw(16)
|
||||
<< std::hex << i;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
u64 fnv1a(std::string s) {
|
||||
u64 hash = 0xCBF29CE484222325LL;
|
||||
for(char& c : s) {
|
||||
hash ^= u64(c);
|
||||
hash *= 0x100000001B3LL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
10
c/utils.h
10
c/utils.h
@@ -1,10 +0,0 @@
|
||||
#ifndef __UTILS_H
|
||||
#define __UTILS_H
|
||||
|
||||
#include "circom.h"
|
||||
|
||||
std::string int_to_hex( u64 i );
|
||||
u64 fnv1a(std::string s);
|
||||
|
||||
|
||||
#endif // __UTILS_H
|
||||
199
c/zqfield.cpp
199
c/zqfield.cpp
@@ -1,199 +0,0 @@
|
||||
#include "zqfield.h"
|
||||
|
||||
ZqField::ZqField(PBigInt ap) {
|
||||
mpz_init_set(p, *ap);
|
||||
mpz_init_set_ui(zero, 0);
|
||||
mpz_init_set_ui(one, 1);
|
||||
nBits = mpz_sizeinbase (p, 2);
|
||||
mpz_init(mask);
|
||||
mpz_mul_2exp(mask, one, nBits-1);
|
||||
mpz_sub(mask, mask, one);
|
||||
}
|
||||
|
||||
ZqField::~ZqField() {
|
||||
mpz_clear(p);
|
||||
mpz_clear(zero);
|
||||
mpz_clear(one);
|
||||
}
|
||||
|
||||
void ZqField::add(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_add(*r,*a,*b);
|
||||
if (mpz_cmp(*r, p) >= 0) {
|
||||
mpz_sub(*r, *r, p);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::sub(PBigInt r, PBigInt a, PBigInt b) {
|
||||
if (mpz_cmp(*a, *b) >= 0) {
|
||||
mpz_sub(*r, *a, *b);
|
||||
} else {
|
||||
mpz_sub(*r, *b, *a);
|
||||
mpz_sub(*r, p, *r);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::neg(PBigInt r, PBigInt a) {
|
||||
if (mpz_sgn(*a) > 0) {
|
||||
mpz_sub(*r, p, *a);
|
||||
} else {
|
||||
mpz_set(*r, *a);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::mul(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_t tmp;
|
||||
mpz_init(tmp);
|
||||
mpz_mul(tmp,*a,*b);
|
||||
mpz_fdiv_r(*r, tmp, p);
|
||||
mpz_clear(tmp);
|
||||
}
|
||||
|
||||
void ZqField::div(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_t tmp;
|
||||
mpz_init(tmp);
|
||||
mpz_invert(tmp, *b, p);
|
||||
mpz_mul(tmp,*a,tmp);
|
||||
mpz_fdiv_r(*r, tmp, p);
|
||||
mpz_clear(tmp);
|
||||
}
|
||||
|
||||
void ZqField::idiv(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_fdiv_q(*r, *a, *b);
|
||||
}
|
||||
|
||||
void ZqField::mod(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_fdiv_r(*r, *a, *b);
|
||||
}
|
||||
|
||||
void ZqField::pow(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_powm(*r, *a, *b, p);
|
||||
}
|
||||
|
||||
void ZqField::lt(PBigInt r, PBigInt a, PBigInt b) {
|
||||
int c = mpz_cmp(*a, *b);
|
||||
if (c<0) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::eq(PBigInt r, PBigInt a, PBigInt b) {
|
||||
int c = mpz_cmp(*a, *b);
|
||||
if (c==0) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::gt(PBigInt r, PBigInt a, PBigInt b) {
|
||||
int c = mpz_cmp(*a, *b);
|
||||
if (c>0) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::leq(PBigInt r, PBigInt a, PBigInt b) {
|
||||
int c = mpz_cmp(*a, *b);
|
||||
if (c<=0) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::geq(PBigInt r, PBigInt a, PBigInt b) {
|
||||
int c = mpz_cmp(*a, *b);
|
||||
if (c>=0) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::neq(PBigInt r, PBigInt a, PBigInt b) {
|
||||
int c = mpz_cmp(*a, *b);
|
||||
if (c!=0) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::land(PBigInt r, PBigInt a, PBigInt b) {
|
||||
if (mpz_sgn(*a) && mpz_sgn(*b)) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::lor(PBigInt r, PBigInt a, PBigInt b) {
|
||||
if (mpz_sgn(*a) || mpz_sgn(*b)) {
|
||||
mpz_set(*r, one);
|
||||
} else {
|
||||
mpz_set(*r, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::lnot(PBigInt r, PBigInt a) {
|
||||
if (mpz_sgn(*a)) {
|
||||
mpz_set(*r, zero);
|
||||
} else {
|
||||
mpz_set(*r, one);
|
||||
}
|
||||
}
|
||||
|
||||
int ZqField::isTrue(PBigInt a) {
|
||||
return mpz_sgn(*a);
|
||||
}
|
||||
|
||||
void ZqField::copyn(PBigInt a, PBigInt b, int n) {
|
||||
for (int i=0;i<n; i++) mpz_set(a[i], b[i]);
|
||||
}
|
||||
|
||||
void ZqField::band(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_and(*r, *a, *b);
|
||||
mpz_and(*r, *r, mask);
|
||||
}
|
||||
|
||||
void ZqField::bor(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_ior(*r, *a, *b);
|
||||
mpz_and(*r, *r, mask);
|
||||
}
|
||||
|
||||
void ZqField::bxor(PBigInt r, PBigInt a, PBigInt b) {
|
||||
mpz_xor(*r, *a, *b);
|
||||
mpz_and(*r, *r, mask);
|
||||
}
|
||||
|
||||
void ZqField::bnot(PBigInt r, PBigInt a) {
|
||||
mpz_xor(*r, *a, mask);
|
||||
mpz_and(*r, *r, mask);
|
||||
}
|
||||
|
||||
void ZqField::shl(PBigInt r, PBigInt a, PBigInt b) {
|
||||
if (mpz_cmp_ui(*b, nBits) >= 0) {
|
||||
mpz_set(*r, zero);
|
||||
} else {
|
||||
mpz_mul_2exp(*r, *a, mpz_get_ui(*b));
|
||||
mpz_and(*r, *r, mask);
|
||||
}
|
||||
}
|
||||
|
||||
void ZqField::shr(PBigInt r, PBigInt a, PBigInt b) {
|
||||
if (mpz_cmp_ui(*b, nBits) >= 0) {
|
||||
mpz_set(*r, zero);
|
||||
} else {
|
||||
mpz_tdiv_q_2exp(*r, *a, mpz_get_ui(*b));
|
||||
mpz_and(*r, *r, mask);
|
||||
}
|
||||
}
|
||||
|
||||
int ZqField::toInt(PBigInt a) {
|
||||
return mpz_get_si (*a);
|
||||
}
|
||||
|
||||
49
c/zqfield.h
49
c/zqfield.h
@@ -1,49 +0,0 @@
|
||||
#ifndef ZQFIELD_H
|
||||
#define ZQFIELD_H
|
||||
|
||||
#include "circom.h"
|
||||
|
||||
class ZqField {
|
||||
public:
|
||||
BigInt p;
|
||||
BigInt one;
|
||||
BigInt zero;
|
||||
size_t nBits;
|
||||
BigInt mask;
|
||||
ZqField(PBigInt ap);
|
||||
~ZqField();
|
||||
|
||||
void copyn(PBigInt a, PBigInt b, int n);
|
||||
|
||||
void add(PBigInt r,PBigInt a, PBigInt b);
|
||||
void sub(PBigInt r,PBigInt a, PBigInt b);
|
||||
void neg(PBigInt r,PBigInt a);
|
||||
void mul(PBigInt r,PBigInt a, PBigInt b);
|
||||
void div(PBigInt r,PBigInt a, PBigInt b);
|
||||
void idiv(PBigInt r,PBigInt a, PBigInt b);
|
||||
void mod(PBigInt r,PBigInt a, PBigInt b);
|
||||
void pow(PBigInt r,PBigInt a, PBigInt b);
|
||||
|
||||
void lt(PBigInt r, PBigInt a, PBigInt b);
|
||||
void eq(PBigInt r, PBigInt a, PBigInt b);
|
||||
void gt(PBigInt r, PBigInt a, PBigInt b);
|
||||
void leq(PBigInt r, PBigInt a, PBigInt b);
|
||||
void geq(PBigInt r, PBigInt a, PBigInt b);
|
||||
void neq(PBigInt r, PBigInt a, PBigInt b);
|
||||
|
||||
void land(PBigInt r, PBigInt a, PBigInt b);
|
||||
void lor(PBigInt r, PBigInt a, PBigInt b);
|
||||
void lnot(PBigInt r, PBigInt a);
|
||||
|
||||
void band(PBigInt r, PBigInt a, PBigInt b);
|
||||
void bor(PBigInt r, PBigInt a, PBigInt b);
|
||||
void bxor(PBigInt r, PBigInt a, PBigInt b);
|
||||
void bnot(PBigInt r, PBigInt a);
|
||||
void shl(PBigInt r, PBigInt a, PBigInt b);
|
||||
void shr(PBigInt r, PBigInt a, PBigInt b);
|
||||
|
||||
int isTrue(PBigInt a);
|
||||
int toInt(PBigInt a);
|
||||
};
|
||||
|
||||
#endif // ZQFIELD_H
|
||||
49
circuits/gates.circom
Normal file
49
circuits/gates.circom
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
template XOR() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a + b - 2*a*b;
|
||||
}
|
||||
|
||||
template AND() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a*b;
|
||||
}
|
||||
|
||||
template OR() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a + b - a*b;
|
||||
}
|
||||
|
||||
template NOT() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
out <== 1 + in - 2*in;
|
||||
}
|
||||
|
||||
template NAND() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== 1 - a*b;
|
||||
}
|
||||
|
||||
template NOR() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a*b + 1 - a - b;
|
||||
}
|
||||
|
||||
|
||||
74
circuits/multiplexer.circom
Normal file
74
circuits/multiplexer.circom
Normal file
@@ -0,0 +1,74 @@
|
||||
// --> Assignation without constraint
|
||||
// <-- Assignation without constraint
|
||||
// === Constraint
|
||||
// <== Assignation with constraint
|
||||
// ==> Assignation with constraint
|
||||
// All variables are members of the field F[p]
|
||||
// https://github.com/zcash-hackworks/sapling-crypto
|
||||
// https://github.com/ebfull/bellman
|
||||
|
||||
/*
|
||||
function log2(a) {
|
||||
if (a==0) {
|
||||
return 0;
|
||||
}
|
||||
let n = 1;
|
||||
let r = 1;
|
||||
while (n<a) {
|
||||
r++;
|
||||
n *= 2;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
*/
|
||||
|
||||
template EscalarProduct(w) {
|
||||
signal input in1[w];
|
||||
signal input in2[w];
|
||||
signal output out;
|
||||
signal aux[w];
|
||||
var lc = 0;
|
||||
for (var i=0; i<w; i++) {
|
||||
aux[i] <== in1[i]*in2[i];
|
||||
lc = lc + aux[i];
|
||||
}
|
||||
out <== lc;
|
||||
}
|
||||
|
||||
template Decoder(w) {
|
||||
signal input inp;
|
||||
signal output out[w];
|
||||
signal output success;
|
||||
var lc=0;
|
||||
|
||||
for (var i=0; i<w; i++) {
|
||||
out[i] <-- (inp == i) ? 1 : 0;
|
||||
out[i] * (inp-i) === 0;
|
||||
lc = lc + out[i];
|
||||
}
|
||||
|
||||
lc ==> success;
|
||||
success * (success -1) === 0;
|
||||
}
|
||||
|
||||
|
||||
template Multiplexor(wIn, nIn) {
|
||||
signal input inp[nIn][wIn];
|
||||
signal input sel;
|
||||
signal output out[wIn];
|
||||
component Decoder(nIn) dec;
|
||||
component EscalarProduct(nIn) ep[wIn];
|
||||
sel ==> dec.inp;
|
||||
for (var j=0; j<wIn; j++) {
|
||||
for (var k=0; k<nIn; k++) {
|
||||
inp[k][j] ==> ep[j].in1[k];
|
||||
dec.out[k] ==> ep[j].in2[k];
|
||||
}
|
||||
ep[j].out ==> out[j];
|
||||
}
|
||||
dec.success === 1;
|
||||
}
|
||||
|
||||
component Multiplexor(8,3) main;
|
||||
|
||||
|
||||
74
circuits/sha256/binsum.circom
Normal file
74
circuits/sha256/binsum.circom
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
|
||||
Binary Sum
|
||||
==========
|
||||
|
||||
This component creates a binary sum componet of ops operands and n bits each operand.
|
||||
|
||||
e is Number of carries: Depends on the number of operands in the input.
|
||||
|
||||
Main Constraint:
|
||||
in[0][0] * 2^0 + in[0][1] * 2^1 + ..... + in[0][n-1] * 2^(n-1) +
|
||||
+ in[1][0] * 2^0 + in[1][1] * 2^1 + ..... + in[1][n-1] * 2^(n-1) +
|
||||
+ ..
|
||||
+ in[ops-1][0] * 2^0 + in[ops-1][1] * 2^1 + ..... + in[ops-1][n-1] * 2^(n-1) +
|
||||
===
|
||||
out[0] * 2^0 + out[1] * 2^1 + + out[n+e-1] *2(n+e-1)
|
||||
|
||||
To waranty binary outputs:
|
||||
|
||||
out[0] * (out[0] - 1) === 0
|
||||
out[1] * (out[0] - 1) === 0
|
||||
.
|
||||
.
|
||||
.
|
||||
out[n+e-1] * (out[n+e-1] - 1) == 0
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
This function calculates the number of extra bits in the output to do the full sum.
|
||||
*/
|
||||
|
||||
function nbits(a) {
|
||||
var n = 1;
|
||||
var r = 0;
|
||||
while (n-1<a) {
|
||||
r++;
|
||||
n *= 2;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
template BinSum(n, ops) {
|
||||
var nout = nbits((2**n -1)*ops);
|
||||
signal input in[ops][n];
|
||||
signal output out[nout];
|
||||
|
||||
var lin = 0;
|
||||
var lout = 0;
|
||||
|
||||
var k;
|
||||
var j;
|
||||
|
||||
for (k=0; k<n; k++) {
|
||||
for (j=0; j<ops; j++) {
|
||||
lin += in[j][k] * 2**k;
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<nout; k++) {
|
||||
out[k] <-- (lin >> k) & 1;
|
||||
|
||||
// Ensure out is binary
|
||||
out[k] * (out[k] - 1) === 0;
|
||||
|
||||
lout += out[k] * 2**k;
|
||||
}
|
||||
|
||||
// Ensure the sum;
|
||||
|
||||
lin === lout;
|
||||
}
|
||||
28
circuits/sha256/bitify.circom
Normal file
28
circuits/sha256/bitify.circom
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
template Num2Bits(n) {
|
||||
signal input in;
|
||||
signal output out[n];
|
||||
var lc1=0;
|
||||
|
||||
for (var i = 0; i<n; i++) {
|
||||
out[i] <-- (in >> i) & 1;
|
||||
out[i] * (out[i] -1 ) === 0;
|
||||
lc1 += out[i] * 2**i;
|
||||
}
|
||||
|
||||
lc1 === in;
|
||||
|
||||
}
|
||||
|
||||
template Bits2Num(n) {
|
||||
signal input in[n];
|
||||
signal output out;
|
||||
var lc1=0;
|
||||
|
||||
for (var i = 0; i<n; i++) {
|
||||
lc1 += in[i] * 2**i;
|
||||
}
|
||||
|
||||
lc1 ==> out;
|
||||
}
|
||||
27
circuits/sha256/ch.circom
Normal file
27
circuits/sha256/ch.circom
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Ch
|
||||
|
||||
000 0
|
||||
001 1
|
||||
010 0
|
||||
011 1
|
||||
100 0
|
||||
101 0
|
||||
110 1
|
||||
111 1
|
||||
|
||||
out = a&b ^ (!a)&c =>
|
||||
|
||||
out = a*(b-c) + c
|
||||
|
||||
*/
|
||||
|
||||
template Ch(n) {
|
||||
signal input a[n];
|
||||
signal input b[n];
|
||||
signal input c[n];
|
||||
signal output out[n];
|
||||
|
||||
for (var k=0; k<n; k++) {
|
||||
out[k] <== a[k] * (b[k]-c[k]) + c[k];
|
||||
}
|
||||
}
|
||||
35
circuits/sha256/constants.circom
Normal file
35
circuits/sha256/constants.circom
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
template H(x) {
|
||||
signal output out[32];
|
||||
var c = [0x6a09e667,
|
||||
0xbb67ae85,
|
||||
0x3c6ef372,
|
||||
0xa54ff53a,
|
||||
0x510e527f,
|
||||
0x9b05688c,
|
||||
0x1f83d9ab,
|
||||
0x5be0cd19];
|
||||
|
||||
for (var i=0; i<32; i++) {
|
||||
out[i] <== (c[x] >> i) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
template K(x) {
|
||||
signal output out[32];
|
||||
var c = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
];
|
||||
|
||||
for (var i=0; i<32; i++) {
|
||||
out[i] <== (c[x] >> i) & 1;
|
||||
}
|
||||
}
|
||||
49
circuits/sha256/gates.circom
Normal file
49
circuits/sha256/gates.circom
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
template XOR() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a + b - 2*a*b;
|
||||
}
|
||||
|
||||
template AND() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a*b;
|
||||
}
|
||||
|
||||
template OR() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a + b - a*b;
|
||||
}
|
||||
|
||||
template NOT() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
out <== 1 + in - 2*in;
|
||||
}
|
||||
|
||||
template NAND() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== 1 - a*b;
|
||||
}
|
||||
|
||||
template NOR() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
out <== a*b + 1 - a - b;
|
||||
}
|
||||
|
||||
|
||||
15
circuits/sha256/main.circom
Normal file
15
circuits/sha256/main.circom
Normal file
@@ -0,0 +1,15 @@
|
||||
include "sha256_2.jaz";
|
||||
|
||||
template Main() {
|
||||
signal private input a;
|
||||
signal private input b;
|
||||
signal output out;
|
||||
|
||||
component sha256_2 = SHA256_2();
|
||||
|
||||
sha256_2.a <== a;
|
||||
sha256_2.b <== a;
|
||||
out <== sha256_2.out;
|
||||
}
|
||||
|
||||
component main = Main();
|
||||
25
circuits/sha256/maj.circom
Normal file
25
circuits/sha256/maj.circom
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Maj function for sha256
|
||||
|
||||
out = a&b ^ a&c ^ b&c =>
|
||||
|
||||
out = a*b + a*c + b*c - 2*a*b*c =>
|
||||
|
||||
out = a*( b + c - 2*b*c ) + b*c =>
|
||||
|
||||
mid = b*c
|
||||
out = a*( b + c - 2*mid ) + mid
|
||||
|
||||
*/
|
||||
|
||||
template Maj(n) {
|
||||
signal input a[n];
|
||||
signal input b[n];
|
||||
signal input c[n];
|
||||
signal output out[n];
|
||||
signal mid[n];
|
||||
|
||||
for (var k=0; k<n; k++) {
|
||||
mid[k] <== b[k]*c[k];
|
||||
out[k] <== a[k] * (b[k]+c[k]-2*mid[k]) + mid[k];
|
||||
}
|
||||
}
|
||||
10
circuits/sha256/rotate.circom
Normal file
10
circuits/sha256/rotate.circom
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
template RotR(n, r) {
|
||||
signal input in[n];
|
||||
signal output out[n];
|
||||
|
||||
for (var i=0; i<n; i++) {
|
||||
out[i] <== in[ (i+r)%n ];
|
||||
}
|
||||
}
|
||||
49
circuits/sha256/sha256_2.circom
Normal file
49
circuits/sha256/sha256_2.circom
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
include "sha256compression.circom";
|
||||
include "bitify.circom"
|
||||
|
||||
template Sha256_2() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
component bits2num = Bits2Num(216);
|
||||
component num2bits[2];
|
||||
|
||||
num2bits[0] = Num2Bits(216);
|
||||
num2bits[1] = Num2Bits(216);
|
||||
|
||||
num2bits[0].in <== a;
|
||||
num2bits[1].in <== b;
|
||||
|
||||
component sha256compression = Sha256compression() ;
|
||||
|
||||
var i;
|
||||
|
||||
for (i=0; i<216; i++) {
|
||||
sha256compression.inp[i] <== num2bits[0].out[215-i];
|
||||
sha256compression.inp[i+216] <== num2bits[1].out[215-i];
|
||||
}
|
||||
|
||||
sha256compression.inp[432] <== 1;
|
||||
|
||||
for (i=433; i<503; i++) {
|
||||
sha256compression.inp[i] <== 0;
|
||||
}
|
||||
|
||||
sha256compression.inp[503] <== 1;
|
||||
sha256compression.inp[504] <== 1;
|
||||
sha256compression.inp[505] <== 0;
|
||||
sha256compression.inp[506] <== 1;
|
||||
sha256compression.inp[507] <== 1;
|
||||
sha256compression.inp[508] <== 0;
|
||||
sha256compression.inp[509] <== 0;
|
||||
sha256compression.inp[510] <== 0;
|
||||
sha256compression.inp[511] <== 0;
|
||||
|
||||
for (i=0; i<216; i++) {
|
||||
bits2num.in[i] <== sha256compression.out[255-i];
|
||||
}
|
||||
|
||||
out <== bits2num.out;
|
||||
}
|
||||
146
circuits/sha256/sha256compression.circom
Normal file
146
circuits/sha256/sha256compression.circom
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
include "constants.circom";
|
||||
include "t1.circom";
|
||||
include "t2.circom";
|
||||
include "binsum.circom";
|
||||
include "sigmaplus.circom";
|
||||
|
||||
template Sha256compression() {
|
||||
signal input inp[512];
|
||||
signal output out[256];
|
||||
signal a[65][32];
|
||||
signal b[65][32];
|
||||
signal c[65][32];
|
||||
signal d[65][32];
|
||||
signal e[65][32];
|
||||
signal f[65][32];
|
||||
signal g[65][32];
|
||||
signal h[65][32];
|
||||
signal w[64][32];
|
||||
|
||||
var i;
|
||||
|
||||
component sigmaPlus[48];
|
||||
for (i=0; i<48; i++) sigmaPlus[i] = SigmaPlus();
|
||||
|
||||
component ct_k[64];
|
||||
for (i=0; i<64; i++) ct_k[i] = K(i);
|
||||
|
||||
component ha0 = H(0);
|
||||
component hb0 = H(1);
|
||||
component hc0 = H(2);
|
||||
component hd0 = H(3);
|
||||
component he0 = H(4);
|
||||
component hf0 = H(5);
|
||||
component hg0 = H(6);
|
||||
component hh0 = H(7);
|
||||
|
||||
component t1[64];
|
||||
for (i=0; i<64; i++) t1[i] = T1();
|
||||
|
||||
component t2[64];
|
||||
for (i=0; i<64; i++) t2[i] = T2();
|
||||
|
||||
component suma[64];
|
||||
for (i=0; i<64; i++) suma[i] = BinSum(32, 2);
|
||||
|
||||
component sume[64];
|
||||
for (i=0; i<64; i++) sume[i] = BinSum(32, 2);
|
||||
|
||||
component fsum[8];
|
||||
for (i=0; i<8; i++) fsum[i] = BinSum(32, 2);
|
||||
|
||||
var k;
|
||||
var t;
|
||||
|
||||
for (t=0; t<64; t++) {
|
||||
if (t<16) {
|
||||
for (k=0; k<32; k++) {
|
||||
w[t][k] <== inp[t*32+31-k];
|
||||
}
|
||||
} else {
|
||||
for (k=0; k<32; k++) {
|
||||
sigmaPlus[t-16].in2[k] <== w[t-2][k];
|
||||
sigmaPlus[t-16].in7[k] <== w[t-7][k];
|
||||
sigmaPlus[t-16].in15[k] <== w[t-15][k];
|
||||
sigmaPlus[t-16].in16[k] <== w[t-16][k];
|
||||
w[t][k] <== sigmaPlus[t-16].out[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++ ) {
|
||||
a[0][k] <== ha0.out[k]
|
||||
b[0][k] <== hb0.out[k]
|
||||
c[0][k] <== hc0.out[k]
|
||||
d[0][k] <== hd0.out[k]
|
||||
e[0][k] <== he0.out[k]
|
||||
f[0][k] <== hf0.out[k]
|
||||
g[0][k] <== hg0.out[k]
|
||||
h[0][k] <== hh0.out[k]
|
||||
}
|
||||
|
||||
for (t = 0; t<64; t++) {
|
||||
for (k=0; k<32; k++) {
|
||||
t1[t].h[k] <== h[t][k];
|
||||
t1[t].e[k] <== e[t][k];
|
||||
t1[t].f[k] <== f[t][k];
|
||||
t1[t].g[k] <== g[t][k];
|
||||
t1[t].k[k] <== ct_k[t].out[k];
|
||||
t1[t].w[k] <== w[t][k];
|
||||
|
||||
t2[t].a[k] <== a[t][k];
|
||||
t2[t].b[k] <== b[t][k];
|
||||
t2[t].c[k] <== c[t][k];
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
sume[t].in[0][k] <== d[t][k];
|
||||
sume[t].in[1][k] <== t1[t].out[k];
|
||||
|
||||
suma[t].in[0][k] <== t1[t].out[k];
|
||||
suma[t].in[1][k] <== t2[t].out[k];
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
h[t+1][k] <== g[t][k];
|
||||
g[t+1][k] <== f[t][k];
|
||||
f[t+1][k] <== e[t][k];
|
||||
e[t+1][k] <== sume[t].out[k];
|
||||
d[t+1][k] <== c[t][k];
|
||||
c[t+1][k] <== b[t][k];
|
||||
b[t+1][k] <== a[t][k];
|
||||
a[t+1][k] <== suma[t].out[k];
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
fsum[0].in[0][k] <== ha0.out[k];
|
||||
fsum[0].in[1][k] <== a[64][k];
|
||||
fsum[1].in[0][k] <== hb0.out[k];
|
||||
fsum[1].in[1][k] <== b[64][k];
|
||||
fsum[2].in[0][k] <== hc0.out[k];
|
||||
fsum[2].in[1][k] <== c[64][k];
|
||||
fsum[3].in[0][k] <== hd0.out[k];
|
||||
fsum[3].in[1][k] <== d[64][k];
|
||||
fsum[4].in[0][k] <== he0.out[k];
|
||||
fsum[4].in[1][k] <== e[64][k];
|
||||
fsum[5].in[0][k] <== hf0.out[k];
|
||||
fsum[5].in[1][k] <== f[64][k];
|
||||
fsum[6].in[0][k] <== hg0.out[k];
|
||||
fsum[6].in[1][k] <== g[64][k];
|
||||
fsum[7].in[0][k] <== hh0.out[k];
|
||||
fsum[7].in[1][k] <== h[64][k];
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
out[31-k] <== fsum[0].out[k];
|
||||
out[32+31-k] <== fsum[1].out[k];
|
||||
out[64+31-k] <== fsum[2].out[k];
|
||||
out[96+31-k] <== fsum[3].out[k];
|
||||
out[128+31-k] <== fsum[4].out[k];
|
||||
out[160+31-k] <== fsum[5].out[k];
|
||||
out[192+31-k] <== fsum[6].out[k];
|
||||
out[224+31-k] <== fsum[7].out[k];
|
||||
}
|
||||
}
|
||||
14
circuits/sha256/shift.circom
Normal file
14
circuits/sha256/shift.circom
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
template ShR(n, r) {
|
||||
signal input in[n];
|
||||
signal output out[n];
|
||||
|
||||
for (var i=0; i<n; i++) {
|
||||
if (i+r >= n) {
|
||||
out[i] <== 0;
|
||||
} else {
|
||||
out[i] <== in[ i+r ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
circuits/sha256/sigma.circom
Normal file
49
circuits/sha256/sigma.circom
Normal file
@@ -0,0 +1,49 @@
|
||||
include "xor3.circom";
|
||||
include "rotate.circom";
|
||||
include "shift.circom";
|
||||
|
||||
template SmallSigma(ra, rb, rc) {
|
||||
signal input in[32];
|
||||
signal output out[32];
|
||||
|
||||
component xor3 = Xor3(32);
|
||||
|
||||
component rota = RotR(32, ra);
|
||||
component rotb = RotR(32, rb);
|
||||
component shrc = ShR(32, rc);
|
||||
|
||||
for (var k=0; k<32; k++) {
|
||||
rota.in[k] <== in[k];
|
||||
rotb.in[k] <== in[k];
|
||||
shrc.in[k] <== in[k];
|
||||
|
||||
xor3.a[k] <== rota.out[k];
|
||||
xor3.b[k] <== rotb.out[k];
|
||||
xor3.c[k] <== shrc.out[k];
|
||||
|
||||
out[k] <== xor3.out[k];
|
||||
}
|
||||
}
|
||||
|
||||
template BigSigma(ra, rb, rc) {
|
||||
signal input in[32];
|
||||
signal output out[32];
|
||||
|
||||
component xor3 = Xor3(32);
|
||||
|
||||
component rota = RotR(32, ra);
|
||||
component rotb = RotR(32, rb);
|
||||
component rotc = RotR(32, rc);
|
||||
|
||||
for (var k=0; k<32; k++) {
|
||||
rota.in[k] <== in[k];
|
||||
rotb.in[k] <== in[k];
|
||||
rotc.in[k] <== in[k];
|
||||
|
||||
xor3.a[k] <== rota.out[k];
|
||||
xor3.b[k] <== rotb.out[k];
|
||||
xor3.c[k] <== rotc.out[k];
|
||||
|
||||
out[k] <== xor3.out[k];
|
||||
}
|
||||
}
|
||||
26
circuits/sha256/sigmaplus.circom
Normal file
26
circuits/sha256/sigmaplus.circom
Normal file
@@ -0,0 +1,26 @@
|
||||
include "binsum.circom"
|
||||
include "sigma.circom"
|
||||
|
||||
template SigmaPlus() {
|
||||
signal input in2[32];
|
||||
signal input in7[32];
|
||||
signal input in15[32];
|
||||
signal input in16[32];
|
||||
signal output out[32];
|
||||
|
||||
component sum = BinSum(32, 4);
|
||||
component sigma1 = SmallSigma(17,19,10);
|
||||
component sigma0 = SmallSigma(7, 18, 3);
|
||||
|
||||
for (var k=0; k<32; k++) {
|
||||
sigma1.in[k] <== in2[k];
|
||||
sigma0.in[k] <== in15[k];
|
||||
|
||||
sum.in[0][k] <== sigma1.out[k];
|
||||
sum.in[1][k] <== in7[k];
|
||||
sum.in[2][k] <== sigma0.out[k];
|
||||
sum.in[3][k] <== in16[k];
|
||||
|
||||
out[k] <== sum.out[k];
|
||||
}
|
||||
}
|
||||
33
circuits/sha256/t1.circom
Normal file
33
circuits/sha256/t1.circom
Normal file
@@ -0,0 +1,33 @@
|
||||
include "binsum.circom";
|
||||
include "sigma.circom";
|
||||
include "ch.circom";
|
||||
|
||||
template T1() {
|
||||
signal input h[32];
|
||||
signal input e[32];
|
||||
signal input f[32];
|
||||
signal input g[32];
|
||||
signal input k[32];
|
||||
signal input w[32];
|
||||
signal output out[32];
|
||||
|
||||
component sum = BinSum(32, 5);
|
||||
component ch = Ch(32);
|
||||
|
||||
component bigsigma1 = BigSigma(6, 11, 25);
|
||||
|
||||
for (var ki=0; ki<32; ki++) {
|
||||
bigsigma1.in[ki] <== e[ki];
|
||||
ch.a[ki] <== e[ki];
|
||||
ch.b[ki] <== f[ki];
|
||||
ch.c[ki] <== g[ki]
|
||||
|
||||
sum.in[0][ki] <== h[ki];
|
||||
sum.in[1][ki] <== bigsigma1.out[ki];
|
||||
sum.in[2][ki] <== ch.out[ki];
|
||||
sum.in[3][ki] <== k[ki];
|
||||
sum.in[4][ki] <== w[ki];
|
||||
|
||||
out[ki] <== sum.out[ki];
|
||||
}
|
||||
}
|
||||
28
circuits/sha256/t2.circom
Normal file
28
circuits/sha256/t2.circom
Normal file
@@ -0,0 +1,28 @@
|
||||
include "binsum.circom";
|
||||
include "sigma.circom";
|
||||
include "maj.circom"
|
||||
|
||||
template T2() {
|
||||
signal input a[32];
|
||||
signal input b[32];
|
||||
signal input c[32];
|
||||
signal output out[32];
|
||||
|
||||
component sum = BinSum(32, 2);
|
||||
|
||||
component bigsigma0 = BigSigma(2, 13, 22);
|
||||
component maj = Maj(32);
|
||||
|
||||
for (var k=0; k<32; k++) {
|
||||
|
||||
bigsigma0.in[k] <== a[k];
|
||||
maj.a[k] <== a[k];
|
||||
maj.b[k] <== b[k];
|
||||
maj.c[k] <== c[k];
|
||||
|
||||
sum.in[0][k] <== bigsigma0.out[k];
|
||||
sum.in[1][k] <== maj.out[k];
|
||||
|
||||
out[k] <== sum.out[k];
|
||||
}
|
||||
}
|
||||
25
circuits/sha256/xor3.circom
Normal file
25
circuits/sha256/xor3.circom
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Xor3 function for sha256
|
||||
|
||||
out = a ^ b ^ c =>
|
||||
|
||||
out = a+b+c - 2*a*b - 2*a*c - 2*b*c + 4*a*b*c =>
|
||||
|
||||
out = a*( 1 - 2*b - 2*c + 4*b*c ) + b + c - 2*b*c =>
|
||||
|
||||
mid = b*c
|
||||
out = a*( 1 - 2*b -2*c + 4*mid ) + b + c - 2 * mid
|
||||
|
||||
*/
|
||||
|
||||
template Xor3(n) {
|
||||
signal input a[n];
|
||||
signal input b[n];
|
||||
signal input c[n];
|
||||
signal output out[n];
|
||||
signal mid[n];
|
||||
|
||||
for (var k=0; k<n; k++) {
|
||||
mid[k] <== b[k]*c[k];
|
||||
out[k] <== a[k] * (1 -2*b[k] -2*c[k] +4*mid[k]) + b[k] + c[k] -2*mid[k];
|
||||
}
|
||||
}
|
||||
20
circuits/tobin.circom
Normal file
20
circuits/tobin.circom
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
|
||||
template toBin(n) {
|
||||
signal input inp;
|
||||
signal output out[n];
|
||||
var lc1=0;
|
||||
|
||||
for (var i = 0; i<n; i++) {
|
||||
out[i] <-- (inp >> i) & 1;
|
||||
out[i] * (out[i] -1 ) === 0;
|
||||
lc1 += out[i] * 2**i;
|
||||
}
|
||||
|
||||
lc1 === inp;
|
||||
|
||||
}
|
||||
|
||||
|
||||
component toBin(3) main;
|
||||
116
cli.js
116
cli.js
@@ -3,20 +3,20 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
||||
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||
|
||||
circom is a free software: you can redistribute it and/or modify it
|
||||
jaz is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
||||
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
@@ -30,25 +30,13 @@ const version = require("./package").version;
|
||||
|
||||
const argv = require("yargs")
|
||||
.version(version)
|
||||
.usage("circom [input source circuit file] -o [output definition circuit file] -c [output c file]")
|
||||
.usage("circom -s [input source circuit file] -o [output definition circuit file]")
|
||||
.alias("s", "source")
|
||||
.alias("o", "output")
|
||||
.alias("c", "csource")
|
||||
.alias("w", "wasm")
|
||||
.alias("s", "sym")
|
||||
.alias("r", "r1cs")
|
||||
.alias("n", "newThreadTemplates")
|
||||
.alias("c", "cfile")
|
||||
.require(["s","o"])
|
||||
.help("h")
|
||||
.alias("h", "help")
|
||||
.option("verbose", {
|
||||
alias: "v",
|
||||
type: "boolean",
|
||||
description: "Run with verbose logging"
|
||||
})
|
||||
.option("fast", {
|
||||
alias: "f",
|
||||
type: "boolean",
|
||||
description: "Do not optimize constraints"
|
||||
})
|
||||
.epilogue(`Copyright (C) 2018 0kims association
|
||||
This program comes with ABSOLUTELY NO WARRANTY;
|
||||
This is free software, and you are welcome to redistribute it
|
||||
@@ -56,90 +44,14 @@ const argv = require("yargs")
|
||||
repo directory at https://github.com/iden3/circom `)
|
||||
.argv;
|
||||
|
||||
const fullFileName = path.resolve(process.cwd(), argv.source);
|
||||
|
||||
let inputFile;
|
||||
if (argv._.length == 0) {
|
||||
inputFile = "circuit.circom";
|
||||
} else if (argv._.length == 1) {
|
||||
inputFile = argv._[0];
|
||||
} else {
|
||||
console.log("Only one circuit at a time is permited");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const fullFileName = path.resolve(process.cwd(), inputFile);
|
||||
const fileName = path.basename(fullFileName, ".circom");
|
||||
const cSourceName = typeof(argv.csource) === "string" ? argv.csource : fileName + ".cpp";
|
||||
const wasmName = typeof(argv.wasm) === "string" ? argv.wasm : fileName + ".wasm";
|
||||
const r1csName = typeof(argv.r1cs) === "string" ? argv.r1cs : fileName + ".r1cs";
|
||||
const symName = typeof(argv.sym) === "string" ? argv.sym : fileName + ".sym";
|
||||
|
||||
const options = {};
|
||||
options.reduceConstraints = !argv.fast;
|
||||
options.verbose = argv.verbose || false;
|
||||
if (argv.csource) {
|
||||
options.cSourceWriteStream = fs.createWriteStream(cSourceName);
|
||||
}
|
||||
if (argv.wasm) {
|
||||
options.wasmWriteStream = fs.createWriteStream(wasmName);
|
||||
}
|
||||
if (argv.r1cs) {
|
||||
options.r1csFileName = r1csName;
|
||||
}
|
||||
if (argv.sym) {
|
||||
options.symWriteStream = fs.createWriteStream(symName);
|
||||
}
|
||||
if (argv.newThreadTemplates) {
|
||||
options.newThreadTemplates = new RegExp(argv.newThreadTemplates);
|
||||
}
|
||||
|
||||
compiler(fullFileName, options).then( () => {
|
||||
let cSourceDone = false;
|
||||
let wasmDone = false;
|
||||
let symDone = false;
|
||||
if (options.cSourceWriteStream) {
|
||||
options.cSourceWriteStream.on("finish", () => {
|
||||
cSourceDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
cSourceDone = true;
|
||||
}
|
||||
if (options.wasmWriteStream) {
|
||||
options.wasmWriteStream.on("finish", () => {
|
||||
wasmDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
wasmDone = true;
|
||||
}
|
||||
if (options.symWriteStream) {
|
||||
options.symWriteStream.on("finish", () => {
|
||||
symDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
symDone = true;
|
||||
}
|
||||
function finishIfDone() {
|
||||
if ((cSourceDone)&&(symDone)&&(wasmDone)) {
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
compiler(fullFileName, argv.cfile).then( (cir) => {
|
||||
fs.writeFileSync(argv.output, JSON.stringify(cir, null, 1), "utf8");
|
||||
}, (err) => {
|
||||
// console.log(err);
|
||||
console.log(err.stack);
|
||||
if (err.pos) {
|
||||
console.error(`ERROR at ${err.errFile}:${err.pos.first_line},${err.pos.first_column}-${err.pos.last_line},${err.pos.last_column} ${err.errStr}`);
|
||||
} else {
|
||||
console.log(err.message);
|
||||
if (argv.verbose) console.log(err.stack);
|
||||
}
|
||||
if (err.ast) {
|
||||
console.error(JSON.stringify(err.ast, null, 1));
|
||||
}
|
||||
console.log(err);
|
||||
console.error(`ERROR at ${err.errFile}:${err.pos.first_line},${err.pos.first_column}-${err.pos.last_line},${err.pos.last_column} ${err.errStr}`);
|
||||
console.error(JSON.stringify(err.ast, null, 1));
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,683 +0,0 @@
|
||||
# Binary format for R1CS
|
||||
|
||||
---
|
||||
eip:
|
||||
title: r1cs binary format
|
||||
author: Jordi Baylina <jordi@baylina.cat>
|
||||
discussions-to:
|
||||
status: draft
|
||||
type: Standards Track
|
||||
category: ERC
|
||||
created: 2019-09-24
|
||||
requires:
|
||||
---
|
||||
|
||||
## Simple Summary
|
||||
|
||||
This standard defines a standard format for a binery representation of a r1cs constraint system.
|
||||
|
||||
## Abstract
|
||||
|
||||
|
||||
## Motivation
|
||||
|
||||
The zero knowledge primitives, requires the definition of a statment that wants to be proved. This statment can be expressed as a deterministric program or an algebraic circuit. Lots of primitives like zkSnarks, bulletProofs or aurora, requires to convert this statment to a rank-one constraint system.
|
||||
|
||||
This standard specifies a format for a r1cs and allows the to connect a set of tools that compiles a program or a circuit to r1cs that can be used for the zksnarks or bulletproofs primitives.
|
||||
|
||||
## Specification
|
||||
|
||||
### General considerations
|
||||
|
||||
The standard extension is `.r1cs`
|
||||
|
||||
|
||||
A deterministic program (or circuit) is a program that generates a set of deterministic values given an input. All those values are labeled from l_{0} to l_{n_labels}
|
||||
|
||||
This file defines a map beween l_{i} -> w_{j} and defines a series a R1CS of the form
|
||||
|
||||
$$
|
||||
\left\{ \begin{array}{rclclcl}
|
||||
(a_{0,0}w_0 + a_{0,1}w_1 + ... + a_{0,n}w_{n}) &\cdot& (b_{0,0} w_0 + b_{0,1} w_1 + ... + b_{0,n} w_{n}) &-& (c_{0,0} w_0 + c_{0,1} w_1 + ... + c_{0,n}w_{n}) &=& 0 \\
|
||||
(a_{1,0}w_0 + a_{1,1}w_1 + ... + a_{1,n}w_{n}) &\cdot& (b_{1,0} w_0 + b_{1,1} w_1 + ... + b_{1,n} w_{n}) &-& (c_{1,0} w_0 + c_{1,1}w_1 + ... + c_{1,n}w_{n}) &=& 0 \\
|
||||
...\\
|
||||
(a_{m-1,0}w_0 + a_{m-1,1}w_1 + ... + a_{m-1,n}w_{n}) &\cdot& (b_{m-1,0} w_0 + b_{m-1,1} w_1 + ... + b_{m-1,n} w_{n}) &-& (c_{m-1,0} w_0 + c_{m-1,1}w_1 + ... + c_{m-1,n}w_{n}) &=& 0
|
||||
\end{array} \right.
|
||||
$$
|
||||
|
||||
Wire 0 must be always mapped to label 0 and it's an input forced to value "1" implicitly
|
||||
|
||||
|
||||
### Format of the file
|
||||
|
||||
````
|
||||
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ 72 31 63 73 ┃ Magic "r1cs"
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ 01 00 00 00 ┃ Version 1
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ 03 00 00 00 ┃ Number of Sections
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ sectionType ┃ 8 │ SectionSize ┃
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ Section Content ┃
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┗━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ sectionType ┃ 8 │ SectionSize ┃
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ Section Content ┃
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┗━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
````
|
||||
|
||||
#### Magic Number
|
||||
|
||||
Size: 4 bytes
|
||||
The file start with a constant 4 bytes (magic number) "r1cs"
|
||||
|
||||
```
|
||||
0x72 0x31 0x63 0x73
|
||||
```
|
||||
|
||||
#### Version
|
||||
|
||||
Size: 4 bytes
|
||||
Format: Little-Endian
|
||||
|
||||
For this standard it's fixed to
|
||||
|
||||
```
|
||||
0x01 0x00 0x00 0x00
|
||||
```
|
||||
|
||||
#### Number of Sections
|
||||
|
||||
Size: 4 bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Number of sections contained in the file
|
||||
|
||||
#### SectionType
|
||||
|
||||
Size: 4 bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Type of the section.
|
||||
|
||||
Currently there are 3 types of sections defined:
|
||||
|
||||
* 0x00000001 : Header Section
|
||||
* 0x00000002 : Constraint Section
|
||||
* 0x00000003 : Wire2LabelId Map Section
|
||||
|
||||
If the file contain other types, the format is valid, but they MUST be ignored.
|
||||
|
||||
Any order of the section must be accepted.
|
||||
|
||||
Example:
|
||||
```
|
||||
0x01 0x00 0x00 0x00
|
||||
```
|
||||
|
||||
#### SectionSize
|
||||
|
||||
Size: `ws` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Size in bytes of the section
|
||||
|
||||
### Header Section
|
||||
|
||||
Section Type: 0x01
|
||||
````
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ FieldDefSize ┃ FieldDef ┃ field Id
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ 00 00 00 00 ┃ bigInt Format
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ 4 │ is ┃ Id size ( Normally 4 (32bits))
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ is │ 01 00 00 00 ┃ nWires
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ is │ 01 00 00 00 ┃ nPubOut
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ is │ 01 00 00 00 ┃ nPubIn
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ is │ 01 00 00 00 ┃ nPrvIn
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ is │ 01 00 00 00 ┃ nLabels
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
||||
┃ is │ 01 00 00 00 ┃ mConstraints
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
|
||||
````
|
||||
|
||||
#### fieldDefSize
|
||||
|
||||
Size: 4 bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Size of the field Definition
|
||||
|
||||
Example:
|
||||
```
|
||||
0x00 0x0 0x00 0x00
|
||||
```
|
||||
|
||||
#### fieldDef
|
||||
|
||||
Field dfinition the first 4 bytes are the type in LE. 0x0000001 Ar prime fields.
|
||||
|
||||
For the prime fields, the next bytes are the prime in variable length LE base 256 format.
|
||||
|
||||
NOTE: This number is independent of the bigInt Format defined next
|
||||
|
||||
#### bigInt Format
|
||||
|
||||
Size: 4 bytes
|
||||
Format: Little-Endian
|
||||
|
||||
0 Means that the Big Int are variable size LE.
|
||||
That is the First byte indicates the size and the remaining bytes are the number in little enfian (LSB first) base 256.
|
||||
|
||||
Numbers from 1 to 16383 are fixed size Litle endian format base 256.
|
||||
|
||||
Example:
|
||||
```
|
||||
0x00 0x00 0x00 0x00
|
||||
```
|
||||
|
||||
#### Id Size (is)
|
||||
|
||||
Size: 4 bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Size of the identifiers for wires, labels and constraints. In small circuits this is going to be 4 (32 bits)
|
||||
but can be increaset to 8 for bigger circiuits.
|
||||
|
||||
The only possible numbers are 4 or 8
|
||||
|
||||
|
||||
#### Number of wires
|
||||
|
||||
Size: `is` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Total Number of wires including ONE signal (Index 0).
|
||||
|
||||
#### Number of public outputs
|
||||
|
||||
Size: `is` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Total Number of wires public output wires. They should be starting at idx 1
|
||||
|
||||
#### Number of public inputs
|
||||
|
||||
Size: `is` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Total Number of wires public input wires. They should be starting just after the public output
|
||||
|
||||
#### Number of private inputs
|
||||
|
||||
Size: `is` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Total Number of wires private input wires. They should be starting just after the public inputs
|
||||
|
||||
#### Number of constraints (m)
|
||||
|
||||
Size: `ìs` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Total Number of constraints
|
||||
|
||||
### Constraints section
|
||||
|
||||
Section Type: 0x02
|
||||
|
||||
````
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
|
||||
┃ is │ nA ┃ ╲
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
|
||||
┃ is │ wireId_1 ┃ V │ a_{0,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
||||
┃ is │ wireId_2 ┃ V │ a_{0,wireId_2} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
... ... │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nA ┃ V │ a_{0,wireId_nA} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ nB ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_1 ┃ V │ b_{0,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
|
||||
┃ is │ wireId_2 ┃ V │ b_{0,wireId_2} ┃ ╲
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_0
|
||||
... ... ╱
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nB ┃ V │ b_{0,wireId_nB} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ nC ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_1 ┃ V │ c_{0,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
||||
┃ is │ wireId_2 ┃ V │ c_{0,wireId_2} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
... ... │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nC ┃ V │ c_{0,wireId_nC} ┃ ╱
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
|
||||
╱
|
||||
|
||||
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
|
||||
┃ is │ nA ┃ ╲
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
|
||||
┃ is │ wireId_1 ┃ V │ a_{1,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
||||
┃ is │ wireId_2 ┃ V │ a_{1,wireId_2} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
... ... │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nA ┃ V │ a_{1,wireId_nA} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ nB ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_1 ┃ V │ b_{1,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
|
||||
┃ is │ wireId_2 ┃ V │ b_{1,wireId_2} ┃ ╲
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_1
|
||||
... ... ╱
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nB ┃ V │ b_{1,wireId_nB} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ nC ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_1 ┃ V │ c_{1,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
||||
┃ is │ wireId_2 ┃ V │ c_{1,wireId_2} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
... ... │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nC ┃ V │ c_{1,wireId_nC} ┃ ╱
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
|
||||
╱
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
|
||||
┃ is │ nA ┃ ╲
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
|
||||
┃ is │ wireId_1 ┃ V │ a_{m-1,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
||||
┃ is │ wireId_2 ┃ V │ a_{m-1,wireId_2} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
... ... │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nA ┃ V │ a_{m-1,wireId_nA} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ nB ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_1 ┃ V │ b_{m-1,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
|
||||
┃ is │ wireId_2 ┃ V │ b_{m-1,wireId_2} ┃ ╲
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_{m-1}
|
||||
... ... ╱
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nB ┃ V │ b_{m-1,wireId_nB} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ nC ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_1 ┃ V │ c_{m-1,wireId_1} ┃ │
|
||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
||||
┃ is │ wireId_2 ┃ V │ c_{m-1,wireId_2} ┃ │
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
||||
... ... │
|
||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
||||
┃ is │ wireId_nC ┃ V │ c_{m-1,wireId_nC} ┃ ╱
|
||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
|
||||
╱ ╱
|
||||
````
|
||||
|
||||
|
||||
|
||||
#### Constraints
|
||||
|
||||
Each constraint contains 3 linear combinations A, B, C.
|
||||
|
||||
The constraint is such that:
|
||||
```
|
||||
A*B-C = 0
|
||||
```
|
||||
|
||||
#### Linear combination
|
||||
|
||||
Each linear combination is of the form:
|
||||
|
||||
$$
|
||||
a_{j,0}w_0 + a_{j,1}w_1 + ... + a_{j,n}w_{n}
|
||||
$$
|
||||
|
||||
#### Number of nonZero Factors
|
||||
|
||||
Size: `ìs` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
Total number of non Zero factors in the linear compination.
|
||||
|
||||
The factors MUST be sorted in ascending order.
|
||||
|
||||
#### Factor
|
||||
|
||||
For each factor we have the index of the factor and the value of the factor.
|
||||
|
||||
#### WireId of the factor
|
||||
|
||||
Size: `is` bytes
|
||||
Format: Little-Endian
|
||||
|
||||
WireId of the nonZero Factor
|
||||
|
||||
#### Value of the factor
|
||||
|
||||
The first byte indicate the length N in bytes of the number in the upcoming bytes.
|
||||
|
||||
The next N bytes represent the value in Little Endian format.
|
||||
|
||||
For example, to represent the linear combination:
|
||||
|
||||
$$
|
||||
5w_4 +8w_5 + 260w_{886}
|
||||
$$
|
||||
|
||||
The linear combination would be represented as:
|
||||
|
||||
````
|
||||
┏━━━━━━━━━━━━━━━━━┓
|
||||
┃ 03 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┓
|
||||
┃ 04 00 00 00 ┃ 01 05 ┃
|
||||
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┫
|
||||
┃ 05 00 00 00 ┃ 01 08 ┃
|
||||
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┫
|
||||
┃ 76 03 00 00 ┃ 02 04 01 ┃
|
||||
┗━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┛
|
||||
````
|
||||
|
||||
|
||||
### WireId2LabelId Map Section
|
||||
|
||||
Section Type: 0x03
|
||||
|
||||
````
|
||||
┏━━┳━━━━━━━━━━━━━━━━━━━┳━━┳━━━━━━━━━━━━━━━━━━━┓ ┏━━┳━━━━━━━━━━━━━━━━━━━┓
|
||||
┃is│ labelId of Wire_0 ┃is│ labelId of Wire_1 ┃ ... ┃is│ labelId of Wire_n ┃
|
||||
┗━━┻━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━━━━━━━━━━━━━━━┛ ┗━━┻━━━━━━━━━━━━━━━━━━━┛
|
||||
````
|
||||
|
||||
## Rationale
|
||||
|
||||
Variable size for field elements allows to shrink the size of the file and allows to work with any field.
|
||||
|
||||
Version allows to update the format.
|
||||
|
||||
Have a very good comprasion ratio for sparse r1cs as it's the normal case.
|
||||
|
||||
The motivation of having a map between l and w is that this allows optimizers to calculate equivalent r1cs systems but keeping the original values geneated by the circuit.
|
||||
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
N.A.
|
||||
|
||||
## Test Cases
|
||||
### Example
|
||||
|
||||
Given this r1cs in a 256 bit Field:
|
||||
|
||||
|
||||
$$
|
||||
\left\{ \begin{array}{rclclcl}
|
||||
(3w_5 + 8w_6) &\cdot& (2w_0 + 20w_2 + 12w_3) &-& (5w_0 + 7w_2) &=& 0 \\
|
||||
(4w_1 + 8w_4 + 3w_5) &\cdot& (6w_6 + 44w_3) && &=& 0 \\
|
||||
(4w_6) &\cdot& (6w_0 + 5w_3 + 11s_2) &-& (600w_6) &=& 0
|
||||
\end{array} \right.
|
||||
$$
|
||||
|
||||
And a Wire to label map.
|
||||
|
||||
$$
|
||||
w_0 := l_0 \\
|
||||
w_1 := l_3 \\
|
||||
w_2 := l_{10} \\
|
||||
w_3 := l_{11} \\
|
||||
w_4 := l_{12} \\
|
||||
w_5 := l_{15} \\
|
||||
w_6 := l_{324} \\
|
||||
$$
|
||||
|
||||
The format will be:
|
||||
|
||||
````
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 72 31 63 77 ┃ Magic
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 01 00 00 00 ┃ Version
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 03 00 00 00 ┃ nSections
|
||||
┗━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 01 00 00 00 ┃ 49 00 00 00 00 00 00 00 ┃ SectionType: Header
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
|
||||
┃ 25 00 00 00 ┃ 10 00 00 00 ┃ FieldDefSize FieldDef
|
||||
┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 20 010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430┃
|
||||
┣━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┃ 00 00 00 00 ┃ Big Int format
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 04 00 00 00 ┃ Id Size
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 07 00 00 00 ┃ # of wires
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 01 00 00 00 ┃ # Public Outs
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 02 00 00 00 ┃ # Public Ins
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 03 00 00 00 ┃ # Private Ins
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ e8 03 00 00 ┃ # Labels
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 03 00 00 00 ┃ # Constraints
|
||||
┗━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 02 00 00 00 ┃ 8b 00 00 00 00 00 00 00 ┃ SectionType: Constraints
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓ Constraint 0: (3w_5 + 8w_6) * (2w_0 + 20w_2 + 12w_3) - (5w_0 + 7w_2) = 0
|
||||
┃ 02 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┓
|
||||
┃ 05 00 00 00 ┃ 01 03 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┫
|
||||
┃ 06 00 00 00 ┃ 01 08 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 03 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┓
|
||||
┃ 00 00 00 00 ┃ 01 02 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┫
|
||||
┃ 02 00 00 00 ┃ 01 14 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┫
|
||||
┃ 03 00 00 00 ┃ 01 0C ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 02 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┓
|
||||
┃ 00 00 00 00 ┃ 01 05 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━┫
|
||||
┃ 02 00 00 00 ┃ 01 07 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━┛
|
||||
|
||||
┏━━━━━━━━━━━━━━┓ Constraint 1: (4w_1 + 8w_4 + 3w_5) * (6w_6 + 44w_3) = 0
|
||||
┃ 03 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
|
||||
┃ 01 00 00 00 ┃ 01 04 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
|
||||
┃ 04 00 00 00 ┃ 01 08 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
|
||||
┃ 05 00 00 00 ┃ 01 03 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 02 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
|
||||
┃ 03 00 00 00 ┃ 01 2C ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
|
||||
┃ 06 00 00 00 ┃ 01 06 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 00 00 00 00 ┃
|
||||
┗━━━━━━━━━━━━━━┛
|
||||
|
||||
┏━━━━━━━━━━━━━━┓ Constraint 2: (4w_6) * (6w_0 + 5w_3 + 11w_2) - (600w_6) = 0
|
||||
┃ 01 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
|
||||
┃ 06 00 00 00 ┃ 01 04 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 03 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
|
||||
┃ 00 00 00 00 ┃ 01 06 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
|
||||
┃ 02 00 00 00 ┃ 01 0B ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
|
||||
┃ 03 00 00 00 ┃ 01 05 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 01 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━╋━━━━━━━━━━━━━┓
|
||||
┃ 06 00 00 00 ┃ 02 58 02 ┃
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┛
|
||||
|
||||
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ 03 00 00 00 ┃ 1c 00 00 00 00 00 00 00 ┃ Wire to Label Map
|
||||
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
┏━━━━━━━━━━━━━━┓
|
||||
┃ 00 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 03 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 0a 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 0b 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 0c 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 0f 00 00 00 ┃
|
||||
┣━━━━━━━━━━━━━━┫
|
||||
┃ 44 01 00 00 ┃
|
||||
┗━━━━━━━━━━━━━━┛
|
||||
````
|
||||
|
||||
And the binary representation in Hex:
|
||||
|
||||
````
|
||||
72 31 63 77
|
||||
01 00 00 00
|
||||
03 00 00 00
|
||||
01 00 00 00 49 00 00 00 00 00 00 00
|
||||
25 00 00 00 10 00 00 00
|
||||
20 010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430
|
||||
00 00 00 00
|
||||
04 00 00 00
|
||||
07 00 00 00
|
||||
01 00 00 00
|
||||
02 00 00 00
|
||||
03 00 00 00
|
||||
e8 03 00 00
|
||||
03 00 00 00
|
||||
02 00 00 00 8b 00 00 00 00 00 00 00
|
||||
02 00 00 00
|
||||
05 00 00 00 01 03
|
||||
06 00 00 00 01 08
|
||||
03 00 00 00
|
||||
00 00 00 00 01 02
|
||||
02 00 00 00 01 14
|
||||
03 00 00 00 01 0C
|
||||
02 00 00 00
|
||||
00 00 00 00 01 05
|
||||
02 00 00 00 01 07
|
||||
03 00 00 00
|
||||
01 00 00 00 01 04
|
||||
04 00 00 00 01 08
|
||||
05 00 00 00 01 03
|
||||
02 00 00 00
|
||||
03 00 00 00 01 2C
|
||||
06 00 00 00 01 06
|
||||
00 00 00 00
|
||||
01 00 00 00
|
||||
06 00 00 00 01 04
|
||||
03 00 00 00
|
||||
00 00 00 00 01 06
|
||||
02 00 00 00 01 0B
|
||||
03 00 00 00 01 05
|
||||
01 00 00 00
|
||||
06 00 00 00 02 58 02
|
||||
03 00 00 00 1c 00 00 00 00 00 00 00
|
||||
00 00 00 00
|
||||
03 00 00 00
|
||||
0a 00 00 00
|
||||
0b 00 00 00
|
||||
0c 00 00 00
|
||||
0f 00 00 00
|
||||
44 01 00 00
|
||||
|
||||
````
|
||||
|
||||
## Implementation
|
||||
|
||||
circom will output this format.
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
Binary file not shown.
Binary file not shown.
4
index.js
4
index.js
@@ -1,3 +1 @@
|
||||
module.exports.compiler = require("./src/compiler.js");
|
||||
module.exports.c_tester = require("./src/c_tester.js");
|
||||
module.exports.tester = require("./src/c_tester.js");
|
||||
module.exports = require("./src/compiler.js");
|
||||
|
||||
83
out.c
Normal file
83
out.c
Normal file
@@ -0,0 +1,83 @@
|
||||
// File: ../../circuits/sha256/bitify.circom
|
||||
function Num2Bits(ctx)
|
||||
{
|
||||
ctx.setVar("lc1", [], "0");
|
||||
for (ctx.setVar("i", [], "0");bigInt(ctx.getVar("i",[])).lt(bigInt(ctx.getVar("n",[]))) ? 1 : 0;(ctx.setVar("i", [], bigInt(ctx.getVar("i",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||
{
|
||||
ctx.setSignal("out", [ctx.getVar("i",[])], bigInt(bigInt(ctx.getVar("i",[])).greater(bigInt(256)) ? 0 : bigInt(ctx.getSignal("in", [])).shr(bigInt(ctx.getVar("i",[]))).and(__MASK__)).and(bigInt("1")).and(__MASK__));
|
||||
ctx.assert(bigInt(ctx.getSignal("out", [ctx.getVar("i",[])])).mul(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("i",[])])).add(__P__).sub(bigInt("1")).mod(__P__))).mod(__P__), "0");
|
||||
ctx.setVar("lc1", [], bigInt(ctx.getVar("lc1",[])).add(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("i",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("i",[])), __P__))).mod(__P__))).mod(__P__));
|
||||
}
|
||||
|
||||
ctx.assert(ctx.getVar("lc1",[]), ctx.getSignal("in", []));
|
||||
}
|
||||
|
||||
function Bits2Num(ctx)
|
||||
{
|
||||
ctx.setVar("lc1", [], "0");
|
||||
for (ctx.setVar("i", [], "0");bigInt(ctx.getVar("i",[])).lt(bigInt(ctx.getVar("n",[]))) ? 1 : 0;(ctx.setVar("i", [], bigInt(ctx.getVar("i",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||
{
|
||||
ctx.setVar("lc1", [], bigInt(ctx.getVar("lc1",[])).add(bigInt(bigInt(ctx.getSignal("in", [ctx.getVar("i",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("i",[])), __P__))).mod(__P__))).mod(__P__));
|
||||
}
|
||||
|
||||
ctx.setSignal("out", [], ctx.getVar("lc1",[]));
|
||||
ctx.assert(ctx.getSignal("out", []), ctx.getVar("lc1",[]));
|
||||
}
|
||||
|
||||
|
||||
// File: ../../circuits/sha256/binsum.circom
|
||||
function nbits(ctx) {
|
||||
ctx.setVar("n", [], "1");
|
||||
ctx.setVar("r", [], "0");
|
||||
while (bigInt(bigInt(ctx.getVar("n",[])).add(__P__).sub(bigInt("1")).mod(__P__)).lt(bigInt(ctx.getVar("a",[]))) ? 1 : 0) {
|
||||
(ctx.setVar("r", [], bigInt(ctx.getVar("r",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__);
|
||||
ctx.setVar("n", [], bigInt(ctx.getVar("n",[])).mul(bigInt("2")).mod(__P__));
|
||||
}
|
||||
|
||||
return ctx.getVar("r",[]);;
|
||||
}
|
||||
function BinSum(ctx)
|
||||
{
|
||||
ctx.setVar("nout", [], ctx.callFunction("nbits", [bigInt(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("n",[])), __P__)).add(__P__).sub(bigInt("1")).mod(__P__)).mul(bigInt(ctx.getVar("ops",[]))).mod(__P__)]));
|
||||
ctx.setVar("lin", [], "0");
|
||||
ctx.setVar("lout", [], "0");
|
||||
for (ctx.setVar("k", [], "0");bigInt(ctx.getVar("k",[])).lt(bigInt(ctx.getVar("n",[]))) ? 1 : 0;(ctx.setVar("k", [], bigInt(ctx.getVar("k",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||
{
|
||||
for (ctx.setVar("j", [], "0");bigInt(ctx.getVar("j",[])).lt(bigInt(ctx.getVar("ops",[]))) ? 1 : 0;(ctx.setVar("j", [], bigInt(ctx.getVar("j",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||
{
|
||||
ctx.setVar("lin", [], bigInt(ctx.getVar("lin",[])).add(bigInt(bigInt(ctx.getSignal("in", [ctx.getVar("j",[]),ctx.getVar("k",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("k",[])), __P__))).mod(__P__))).mod(__P__));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (ctx.setVar("k", [], "0");bigInt(ctx.getVar("k",[])).lt(bigInt(ctx.getVar("nout",[]))) ? 1 : 0;(ctx.setVar("k", [], bigInt(ctx.getVar("k",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||
{
|
||||
ctx.setSignal("out", [ctx.getVar("k",[])], bigInt(bigInt(ctx.getVar("k",[])).greater(bigInt(256)) ? 0 : bigInt(ctx.getVar("lin",[])).shr(bigInt(ctx.getVar("k",[]))).and(__MASK__)).and(bigInt("1")).and(__MASK__));
|
||||
ctx.assert(bigInt(ctx.getSignal("out", [ctx.getVar("k",[])])).mul(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("k",[])])).add(__P__).sub(bigInt("1")).mod(__P__))).mod(__P__), "0");
|
||||
ctx.setVar("lout", [], bigInt(ctx.getVar("lout",[])).add(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("k",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("k",[])), __P__))).mod(__P__))).mod(__P__));
|
||||
}
|
||||
|
||||
ctx.assert(ctx.getVar("lin",[]), ctx.getVar("lout",[]));
|
||||
}
|
||||
|
||||
|
||||
function A(ctx)
|
||||
{
|
||||
ctx.setPin("n2ba", [], "in", [], ctx.getSignal("a", []));
|
||||
ctx.assert(ctx.getPin("n2ba", [], "in", []), ctx.getSignal("a", []));
|
||||
ctx.setPin("n2bb", [], "in", [], ctx.getSignal("b", []));
|
||||
ctx.assert(ctx.getPin("n2bb", [], "in", []), ctx.getSignal("b", []));
|
||||
for (ctx.setVar("i", [], "0");bigInt(ctx.getVar("i",[])).lt(bigInt("32")) ? 1 : 0;(ctx.setVar("i", [], bigInt(ctx.getVar("i",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||
{
|
||||
ctx.setPin("sum", [], "in", ["0",ctx.getVar("i",[])], ctx.getPin("n2ba", [], "out", [ctx.getVar("i",[])]));
|
||||
ctx.assert(ctx.getPin("sum", [], "in", ["0",ctx.getVar("i",[])]), ctx.getPin("n2ba", [], "out", [ctx.getVar("i",[])]));
|
||||
ctx.setPin("sum", [], "in", ["1",ctx.getVar("i",[])], ctx.getPin("n2bb", [], "out", [ctx.getVar("i",[])]));
|
||||
ctx.assert(ctx.getPin("sum", [], "in", ["1",ctx.getVar("i",[])]), ctx.getPin("n2bb", [], "out", [ctx.getVar("i",[])]));
|
||||
ctx.setPin("b2n", [], "in", [ctx.getVar("i",[])], ctx.getPin("sum", [], "out", [ctx.getVar("i",[])]));
|
||||
ctx.assert(ctx.getPin("b2n", [], "in", [ctx.getVar("i",[])]), ctx.getPin("sum", [], "out", [ctx.getVar("i",[])]));
|
||||
}
|
||||
|
||||
ctx.setSignal("out", [], ctx.getPin("b2n", [], "out", []));
|
||||
ctx.assert(ctx.getSignal("out", []), ctx.getPin("b2n", [], "out", []));
|
||||
}
|
||||
|
||||
1230
package-lock.json
generated
1230
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "circom",
|
||||
"version": "0.0.35",
|
||||
"version": "0.0.7",
|
||||
"description": "Language to generate logic circuits",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
@@ -30,19 +30,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"big-integer": "^1.6.32",
|
||||
"ejs": "^3.0.1",
|
||||
"fflib": "0.0.2",
|
||||
"fnv-plus": "^1.3.1",
|
||||
"optimist": "^0.6.1",
|
||||
"rimraf-promise": "^2.0.0",
|
||||
"tmp-promise": "^2.0.2",
|
||||
"yargs": "^12.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"chai": "^4.1.2",
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"jison": "^0.4.18",
|
||||
"snarkjs": "0.1.14"
|
||||
"zksnark": "0.0.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
||||
|
||||
circom is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* description: Construct AST for jaz language. */
|
||||
|
||||
/* lexical grammar */
|
||||
@@ -40,7 +21,6 @@ if { return 'if'; }
|
||||
else { return 'else'; }
|
||||
for { return 'for'; }
|
||||
while { return 'while'; }
|
||||
compute { return 'compute'; }
|
||||
do { return 'do'; }
|
||||
return { return 'return'; }
|
||||
include { return 'include'; }
|
||||
@@ -79,13 +59,11 @@ include { return 'include'; }
|
||||
\- { return '-'; }
|
||||
\* { return '*'; }
|
||||
\/ { return '/'; }
|
||||
\\ { return '\\'; }
|
||||
\% { return '%'; }
|
||||
\^ { return '^'; }
|
||||
\& { return '&'; }
|
||||
\| { return '|'; }
|
||||
\! { return '!'; }
|
||||
\~ { return '~'; }
|
||||
\< { return '<'; }
|
||||
\> { return '>'; }
|
||||
\! { return '!'; }
|
||||
@@ -121,7 +99,7 @@ include { return 'include'; }
|
||||
%left '<<' '>>'
|
||||
|
||||
%left '+' '-'
|
||||
%left '*' '/' '\\' '%'
|
||||
%left '*' '/' '%'
|
||||
%left '**'
|
||||
%right '++' '--' UMINUS UPLUS '!' '~'
|
||||
%left '.'
|
||||
@@ -200,10 +178,6 @@ statment
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| computeStatment
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| returnStatment
|
||||
{
|
||||
$$ = $1;
|
||||
@@ -308,14 +282,6 @@ doWhileStatment
|
||||
}
|
||||
;
|
||||
|
||||
computeStatment
|
||||
: 'compute' statment
|
||||
{
|
||||
$$ = { type: "COMPUTE", body: $2 };
|
||||
setLines($$, @1, @2);
|
||||
}
|
||||
;
|
||||
|
||||
returnStatment
|
||||
: 'return' expression ';'
|
||||
{
|
||||
@@ -528,7 +494,7 @@ e12
|
||||
: e12 '^' e11
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
$$ = { type: "NUMBER", value: $1.value.xor($3.value).and(__MASK__) };
|
||||
$$ = { type: "NUMBER", value: $1.value.or($3.value).and(__MASK__) };
|
||||
} else {
|
||||
$$ = { type: "OP", op: "^", values: [$1, $3] };
|
||||
}
|
||||
@@ -642,7 +608,7 @@ e7
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
let v = $3.value.greater(256) ? 256 : $3.value.value;
|
||||
$$ = {type: "NUMBER", value: $1.value.shiftRight(v).and(__MASK__) };
|
||||
$$ = {t1ype: "NUMBER", value: $1.value.shiftRight(v).and(__MASK__) };
|
||||
} else {
|
||||
$$ = { type: "OP", op: ">>", values: [$1, $3] };
|
||||
}
|
||||
@@ -699,15 +665,6 @@ e5
|
||||
}
|
||||
setLines($$, @1, @3);
|
||||
}
|
||||
| e5 '\\' e4
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
$$ = { type: "NUMBER", value: ($1.value.divide($3.value)) };
|
||||
} else {
|
||||
$$ = { type: "OP", op: "\\", values: [$1, $3] };
|
||||
}
|
||||
setLines($$, @1, @3);
|
||||
}
|
||||
| e5 '%' e4
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
|
||||
340
parser/jaz.js
340
parser/jaz.js
File diff suppressed because one or more lines are too long
@@ -1,59 +0,0 @@
|
||||
const SUBARRAY_SIZE = 0x10000;
|
||||
|
||||
const BigArrayHandler = {
|
||||
get: function(obj, prop) {
|
||||
if (!isNaN(prop)) {
|
||||
return obj.getElement(prop);
|
||||
} else return obj[prop];
|
||||
},
|
||||
set: function(obj, prop, value) {
|
||||
if (!isNaN(prop)) {
|
||||
return obj.setElement(prop, value);
|
||||
} else {
|
||||
obj[prop] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class _BigArray {
|
||||
constructor (initSize) {
|
||||
this.length = initSize || 0;
|
||||
this.arr = [];
|
||||
|
||||
for (let i=0; i<initSize; i+=SUBARRAY_SIZE) {
|
||||
this.arr[i/SUBARRAY_SIZE] = new Array(Math.min(SUBARRAY_SIZE, initSize - i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
push (element) {
|
||||
this.setElement (this.length, element);
|
||||
}
|
||||
getElement(idx) {
|
||||
idx = parseInt(idx);
|
||||
const idx1 = Math.floor(idx / SUBARRAY_SIZE);
|
||||
const idx2 = idx % SUBARRAY_SIZE;
|
||||
return this.arr[idx1] ? this.arr[idx1][idx2] : undefined;
|
||||
}
|
||||
setElement(idx, value) {
|
||||
idx = parseInt(idx);
|
||||
const idx1 = Math.floor(idx / SUBARRAY_SIZE);
|
||||
if (!this.arr[idx1]) {
|
||||
this.arr[idx1] = [];
|
||||
}
|
||||
const idx2 = idx % SUBARRAY_SIZE;
|
||||
this.arr[idx1][idx2] = value;
|
||||
if (idx >= this.length) this.length = idx+1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class BigArray {
|
||||
constructor( initSize ) {
|
||||
const obj = new _BigArray(initSize);
|
||||
const extObj = new Proxy(obj, BigArrayHandler);
|
||||
return extObj;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BigArray;
|
||||
468
src/build.js
468
src/build.js
@@ -1,468 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
||||
|
||||
circom is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const assert = require("assert");
|
||||
const bigInt = require("big-integer");
|
||||
const utils = require("./utils");
|
||||
const gen = require("./gencode").gen;
|
||||
const createRefs = require("./gencode").createRefs;
|
||||
|
||||
module.exports = build;
|
||||
|
||||
|
||||
function build(ctx) {
|
||||
ctx.definedFunctions = {};
|
||||
ctx.functionCodes = [];
|
||||
ctx.buildFunction = buildFunction;
|
||||
ctx.conditionalCodeHeader = "";
|
||||
ctx.codes_sizes = [];
|
||||
ctx.definedSizes = {};
|
||||
ctx.addSizes = addSizes;
|
||||
ctx.constantsMap = {};
|
||||
ctx.addConstant = addConstant;
|
||||
ctx.addConstant(bigInt.zero);
|
||||
ctx.addConstant(bigInt.one);
|
||||
|
||||
buildHeader(ctx);
|
||||
buildEntryTables(ctx);
|
||||
ctx.globalNames = ctx.uniqueNames;
|
||||
|
||||
buildCode(ctx);
|
||||
|
||||
buildComponentsArray(ctx);
|
||||
|
||||
buildMapIsInput(ctx);
|
||||
buildWit2Sig(ctx);
|
||||
|
||||
}
|
||||
|
||||
function buildEntryTables(ctx) {
|
||||
|
||||
const codes_hashMaps = [];
|
||||
const codes_componentEntries = [];
|
||||
const definedHashMaps = {};
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const {htName, htMap} = addHashTable(i);
|
||||
|
||||
let code = "";
|
||||
const componentEntriesTableName = ctx.getUniqueName("_entryTable" + ctx.components[i].template);
|
||||
|
||||
const componentEntriesTable = [];
|
||||
for (let j=0; j<htMap.length; j++) {
|
||||
const entry = ctx.components[i].names.o[htMap[j]];
|
||||
const sizeName = ctx.addSizes(entry.sizes);
|
||||
componentEntriesTable.push({
|
||||
offset: entry.offset,
|
||||
sizeName: sizeName,
|
||||
type: entry.type
|
||||
});
|
||||
}
|
||||
|
||||
ctx.builder.addComponentEntriesTable(componentEntriesTableName, componentEntriesTable);
|
||||
|
||||
|
||||
code += `Circom_ComponentEntry ${componentEntriesTableName}[${htMap.length}] = {\n`;
|
||||
for (let j=0; j<htMap.length; j++) {
|
||||
const entry = ctx.components[i].names.o[htMap[j]];
|
||||
code += j>0 ? " ," : " ";
|
||||
const sizeName = ctx.addSizes(entry.sizes);
|
||||
|
||||
const ty = entry.type == "S" ? "_typeSignal" : "_typeComponent";
|
||||
code += `{${entry.offset},${sizeName}, ${ty}}\n`;
|
||||
}
|
||||
code += "};\n";
|
||||
codes_componentEntries.push(code);
|
||||
|
||||
ctx.components[i].htName = htName;
|
||||
ctx.components[i].etName = componentEntriesTableName;
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
"// HashMaps\n" ,
|
||||
codes_hashMaps , "\n" ,
|
||||
"\n" ,
|
||||
"// Component Entries\n" ,
|
||||
codes_componentEntries , "\n" ,
|
||||
"\n"
|
||||
];
|
||||
|
||||
function addHashTable(cIdx) {
|
||||
const keys = Object.keys(ctx.components[cIdx].names.o);
|
||||
assert(keys.length<128);
|
||||
keys.sort((a,b) => ((a>b) ? 1 : -1));
|
||||
const h = utils.fnvHash(keys.join(","));
|
||||
if (definedHashMaps[h]) return definedHashMaps[h];
|
||||
definedHashMaps[h] = {};
|
||||
definedHashMaps[h].htName = ctx.getUniqueName("_ht"+ctx.components[cIdx].template);
|
||||
definedHashMaps[h].htMap = [];
|
||||
const t = [];
|
||||
for (let i=0; i<keys.length; i++) {
|
||||
definedHashMaps[h].htMap[i] = keys[i];
|
||||
|
||||
const h2 = utils.fnvHash(keys[i]);
|
||||
let pos = parseInt(h2.slice(-2), 16);
|
||||
while (t[pos]) pos = (pos + 1) % 256;
|
||||
t[pos] = [h2, i, keys[i]];
|
||||
}
|
||||
ctx.builder.addHashMap(definedHashMaps[h].htName, t);
|
||||
|
||||
return definedHashMaps[h];
|
||||
}
|
||||
}
|
||||
|
||||
function buildCode(ctx) {
|
||||
|
||||
const fDefined = {};
|
||||
|
||||
const fnComponents = [];
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const {h, instanceDef} = hashComponentCall(ctx, i);
|
||||
const fName = ctx.components[i].template+"_"+h;
|
||||
if (!fDefined[fName]) {
|
||||
|
||||
|
||||
ctx.scopes = [{}];
|
||||
ctx.conditionalCode = false;
|
||||
ctx.fnBuilder = ctx.builder.newComponentFunctionBuilder(fName, instanceDef);
|
||||
ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
|
||||
ctx.uniqueNames = Object.assign({},ctx.globalNames);
|
||||
ctx.refs = [];
|
||||
ctx.fileName = ctx.templates[ctx.components[i].template].fileName;
|
||||
ctx.filePath = ctx.templates[ctx.components[i].template].filePath;
|
||||
ctx.getSignalSizesCache = {};
|
||||
ctx.getSignalOffsetCache = {};
|
||||
|
||||
for (let p in ctx.components[i].params) {
|
||||
if (ctx.scopes[0][p]) return ctx.throwError(`Repeated parameter at ${ctx.components[i].template}: ${p}`);
|
||||
const refId = ctx.refs.length;
|
||||
ctx.refs.push({
|
||||
type: "BIGINT",
|
||||
used: false,
|
||||
value: utils.flatArray(ctx.components[i].params[p]),
|
||||
sizes: utils.accSizes(utils.extractSizes(ctx.components[i].params[p])),
|
||||
label: ctx.getUniqueName(p)
|
||||
});
|
||||
ctx.scopes[0][p] = refId;
|
||||
}
|
||||
|
||||
createRefs(ctx, ctx.templates[ctx.components[i].template].block);
|
||||
if (ctx.error) return;
|
||||
|
||||
gen(ctx, ctx.templates[ctx.components[i].template].block);
|
||||
if (ctx.error) return;
|
||||
|
||||
ctx.fnBuilder.setBody(ctx.codeBuilder);
|
||||
|
||||
ctx.builder.addFunction(ctx.fnBuilder);
|
||||
|
||||
fDefined[fName] = true;
|
||||
}
|
||||
ctx.components[i].fnName = fName;
|
||||
}
|
||||
|
||||
return fnComponents;
|
||||
}
|
||||
|
||||
function buildComponentsArray(ctx) {
|
||||
for (let i=0; i< ctx.components.length; i++) {
|
||||
let newThread;
|
||||
if (ctx.newThreadTemplates) {
|
||||
if (ctx.newThreadTemplates.test(ctx.components[i].template)) {
|
||||
newThread = true;
|
||||
} else {
|
||||
newThread = false;
|
||||
}
|
||||
} else {
|
||||
newThread = false;
|
||||
}
|
||||
ctx.builder.addComponent({
|
||||
hashMapName: ctx.components[i].htName,
|
||||
entryTableName: ctx.components[i].etName,
|
||||
functionName: ctx.components[i].fnName,
|
||||
nInSignals: ctx.components[i].nInSignals,
|
||||
newThread: newThread
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function buildHeader(ctx) {
|
||||
ctx.builder.setHeader({
|
||||
NSignals: ctx.signals.length,
|
||||
NComponents: ctx.components.length,
|
||||
NInputs: ctx.components[ ctx.getComponentIdx("main") ].nInSignals,
|
||||
NOutputs: ctx.totals[ ctx.stOUTPUT ],
|
||||
NVars: ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL],
|
||||
P: ctx.field.p
|
||||
});
|
||||
}
|
||||
|
||||
function buildMapIsInput(ctx) {
|
||||
let i;
|
||||
let map = [];
|
||||
let acc = 0;
|
||||
for (i=0; i<ctx.signals.length; i++) {
|
||||
if (ctx.signals[i].o & ctx.IN) {
|
||||
acc = acc | (1 << (i%32) );
|
||||
}
|
||||
if ((i+1)%32==0) {
|
||||
map.push(acc);
|
||||
acc = 0;
|
||||
}
|
||||
}
|
||||
if ((i%32) != 0) {
|
||||
map.push(acc);
|
||||
}
|
||||
|
||||
ctx.builder.setMapIsInput(map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function buildWit2Sig(ctx) {
|
||||
const NVars =
|
||||
ctx.totals[ctx.stONE] +
|
||||
ctx.totals[ctx.stOUTPUT] +
|
||||
ctx.totals[ctx.stPUBINPUT] +
|
||||
ctx.totals[ctx.stPRVINPUT] +
|
||||
ctx.totals[ctx.stINTERNAL];
|
||||
const arr = Array(NVars);
|
||||
for (let i=0; i<ctx.signals.length; i++) {
|
||||
const outIdx = ctx.signals[i].id;
|
||||
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
||||
assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
|
||||
if (outIdx>=NVars) continue; // Is a constant or a discarded variable
|
||||
if (typeof arr[ctx.signals[i].id] == "undefined") {
|
||||
arr[outIdx] = i;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.builder.setWit2Sig(arr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function addSizes(_sizes) {
|
||||
const sizes = _sizes || [];
|
||||
let name = "sizes";
|
||||
for (let i=0; i<sizes.length;i++) {
|
||||
name+="_"+sizes[i];
|
||||
}
|
||||
if (name=="sizes") name="sizes_0";
|
||||
|
||||
if (this.definedSizes[name]) return this.definedSizes[name];
|
||||
const labelName = this.getUniqueName(name);
|
||||
this.definedSizes[name] = labelName;
|
||||
|
||||
const accSizes = utils.accSizes(sizes);
|
||||
|
||||
this.builder.addSizes(labelName, accSizes);
|
||||
|
||||
let code = `Circom_Size ${labelName}[${accSizes.length}] = {`;
|
||||
for (let i=0; i<accSizes.length; i++) {
|
||||
if (i>0) code += ",";
|
||||
code += accSizes[i];
|
||||
}
|
||||
code += "};\n";
|
||||
this.codes_sizes.push(code);
|
||||
|
||||
return labelName;
|
||||
}
|
||||
|
||||
function addConstant(c) {
|
||||
c = bigInt(c);
|
||||
const s = c.toString();
|
||||
if (typeof (this.constantsMap[s]) !== "undefined") return this.constantsMap[s];
|
||||
const cIdx = this.builder.addConstant(c);
|
||||
this.constantsMap[s] = cIdx;
|
||||
return cIdx;
|
||||
}
|
||||
|
||||
function buildFunction(name, paramValues) {
|
||||
const ctx = this;
|
||||
const {h, instanceDef} = hashFunctionCall(ctx, name, paramValues);
|
||||
|
||||
if (ctx.definedFunctions[h]) return ctx.definedFunctions[h];
|
||||
|
||||
const res = {
|
||||
fnName: `${name}_${h}`
|
||||
};
|
||||
|
||||
const oldRefs = ctx.refs;
|
||||
const oldConditionalCode = ctx.conditionalCode;
|
||||
const oldCodeBuilder = ctx.codeBuilder;
|
||||
const oldFnBuilder = ctx.fnBuilder;
|
||||
|
||||
const oldUniqueNames = ctx.uniqueNames;
|
||||
const oldFileName = ctx.fileName;
|
||||
const oldFilePath = ctx.oldFilePath;
|
||||
const oldReturnSizes = ctx.returnSizes;
|
||||
const oldReturnValue = ctx.returnValue;
|
||||
|
||||
|
||||
ctx.scopes = [{}];
|
||||
ctx.refs = [];
|
||||
ctx.conditionalCode = false;
|
||||
ctx.fnBuilder = ctx.builder.newFunctionBuilder(`${name}_${h}`, instanceDef);
|
||||
ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
|
||||
ctx.uniqueNames = Object.assign({},ctx.globalNames);
|
||||
ctx.returnValue = null;
|
||||
ctx.returnSizes = null;
|
||||
ctx.fileName = ctx.functions[name].fileName;
|
||||
ctx.filePath = ctx.functions[name].filePath;
|
||||
|
||||
let paramLabels = [];
|
||||
|
||||
for (let i=0; i<ctx.functions[name].params.length; i++) {
|
||||
|
||||
if (paramValues[i].used) {
|
||||
paramLabels.push(ctx.functions[name].params[i]);
|
||||
const idRef = ctx.refs.length;
|
||||
ctx.refs.push({
|
||||
type: "BIGINT",
|
||||
used: true,
|
||||
sizes: paramValues[i].sizes,
|
||||
label: ctx.functions[name].params[i],
|
||||
});
|
||||
ctx.scopes[0][ctx.functions[name].params[i]] = idRef;
|
||||
} else {
|
||||
const idRef = ctx.refs.length;
|
||||
ctx.refs.push({
|
||||
type: "BIGINT",
|
||||
used: false,
|
||||
sizes: paramValues[i].sizes,
|
||||
label: ctx.functions[name].params[i],
|
||||
value: paramValues[i].value
|
||||
});
|
||||
ctx.scopes[0][ctx.functions[name].params[i]] = idRef;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fnBuilder.setParams(paramLabels);
|
||||
|
||||
createRefs(ctx, ctx.functions[name].block);
|
||||
if (ctx.error) return;
|
||||
|
||||
gen(ctx, ctx.functions[name].block);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (ctx.returnValue == null) {
|
||||
if (ctx.returnSizes == null) assert(false, `Funciont ${name} does not return any value`);
|
||||
|
||||
ctx.fnBuilder.setBody(ctx.codeBuilder);
|
||||
ctx.builder.addFunction(ctx.fnBuilder);
|
||||
|
||||
res.type = "VARVAL_CONSTSIZE";
|
||||
res.returnSizes = ctx.returnSizes;
|
||||
} else {
|
||||
res.type = "CONSTVAL";
|
||||
res.returnValue = ctx.returnValue;
|
||||
res.returnSizes = ctx.returnSizes;
|
||||
}
|
||||
|
||||
ctx.refs = oldRefs;
|
||||
ctx.conditionalCode = oldConditionalCode;
|
||||
ctx.codeBuilder = oldCodeBuilder;
|
||||
ctx.fnBuilder = oldFnBuilder;
|
||||
ctx.uniqueNames = oldUniqueNames;
|
||||
ctx.fileName = oldFileName;
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.returnSizes = oldReturnSizes;
|
||||
ctx.returnValue = oldReturnValue;
|
||||
|
||||
ctx.definedFunctions[h] = res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
function hashComponentCall(ctx, cIdx) {
|
||||
// TODO: At the moment generate a diferent function for each instance of the component
|
||||
const constParams = [];
|
||||
for (let p in ctx.components[cIdx].params) {
|
||||
constParams.push(p + "=" + value2str(ctx.components[cIdx].params[p]));
|
||||
}
|
||||
|
||||
for (let n in ctx.components[cIdx].names.o) {
|
||||
const entry = ctx.components[cIdx].names.o[n];
|
||||
if ((entry.type == "S")&&(ctx.signals[entry.offset].o & ctx.IN)) {
|
||||
travelSizes(n, entry.offset, entry.sizes, (prefix, offset) => {
|
||||
if (utils.isDefined(ctx.signals[offset].v)) {
|
||||
constParams.push(prefix + "=" + bigInt(ctx.signals[offset].value));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let instanceDef = ctx.components[cIdx].template;
|
||||
if (constParams.length>0) {
|
||||
instanceDef += "\n";
|
||||
constParams.sort();
|
||||
instanceDef += constParams.join("\n");
|
||||
}
|
||||
const h = utils.fnvHash(instanceDef);
|
||||
return {h, instanceDef};
|
||||
|
||||
function travelSizes(prefix, offset, sizes, fn) {
|
||||
if (sizes.length == 0) {
|
||||
fn(prefix, offset);
|
||||
return 1;
|
||||
} else {
|
||||
let o = offset;
|
||||
for (let i=0; i<sizes[0]; i++) {
|
||||
o += travelSizes(prefix + "[" + i + "]", o, sizes.slice(1), fn);
|
||||
}
|
||||
return o-offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hashFunctionCall(ctx, name, paramValues) {
|
||||
// TODO
|
||||
const constParams = [];
|
||||
for (let i=0; i<ctx.functions[name].params.length; i++) {
|
||||
if (!paramValues[i].used) {
|
||||
constParams.push(ctx.functions[name].params[i] + utils.accSizes2Str(paramValues[i].sizes) + "=" + value2str(paramValues[i].value));
|
||||
}
|
||||
}
|
||||
let instanceDef = name;
|
||||
if (constParams.length>0) {
|
||||
instanceDef += "\n";
|
||||
constParams.sort();
|
||||
instanceDef += constParams.join("\n");
|
||||
}
|
||||
|
||||
const h = utils.fnvHash(instanceDef);
|
||||
return {h, instanceDef};
|
||||
}
|
||||
|
||||
function value2str(v) {
|
||||
if (Array.isArray(v)) {
|
||||
let S="[";
|
||||
for (let i=0; i<v.length; i++) {
|
||||
if (i>0) S+=",";
|
||||
S+=value2str(v[i]);
|
||||
}
|
||||
S+="]";
|
||||
return S;
|
||||
} else {
|
||||
return bigInt(v).toString();
|
||||
}
|
||||
}
|
||||
616
src/builder_c.js
616
src/builder_c.js
@@ -1,616 +0,0 @@
|
||||
const streamFromMultiArray = require("./stream_from_multiarray");
|
||||
const bigInt = require("big-integer");
|
||||
const utils = require("./utils");
|
||||
const assert = require("assert");
|
||||
|
||||
function ref2src(c) {
|
||||
if (c[0] == "R") {
|
||||
return c[1];
|
||||
} else if (c[0] == "V") {
|
||||
return c[1].toString();
|
||||
} else if (c[0] == "C") {
|
||||
return `(ctx->circuit->constants + ${c[1]})`;
|
||||
} else if (c[0] == "CC") {
|
||||
return "__cIdx";
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
class CodeBuilderC {
|
||||
constructor() {
|
||||
this.ops = [];
|
||||
}
|
||||
|
||||
addComment(comment) {
|
||||
this.ops.push({op: "COMMENT", comment});
|
||||
}
|
||||
|
||||
addBlock(block) {
|
||||
this.ops.push({op: "BLOCK", block});
|
||||
}
|
||||
|
||||
calcOffset(dLabel, offsets) {
|
||||
this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
|
||||
}
|
||||
|
||||
assign(dLabel, src, sOffset) {
|
||||
this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
|
||||
}
|
||||
|
||||
getSubComponentOffset(dLabel, component, hash, hashLabel) {
|
||||
this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
|
||||
}
|
||||
|
||||
getSubComponentSizes(dLabel, component, hash, hashLabel) {
|
||||
this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
|
||||
}
|
||||
|
||||
getSignalOffset(dLabel, component, hash, hashLabel) {
|
||||
this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
|
||||
}
|
||||
|
||||
getSignalSizes(dLabel, component, hash, hashLabel) {
|
||||
this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
|
||||
}
|
||||
|
||||
setSignal(component, signal, value) {
|
||||
this.ops.push({op: "SETSIGNAL", component, signal, value});
|
||||
}
|
||||
|
||||
getSignal(dLabel, component, signal) {
|
||||
this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
|
||||
}
|
||||
|
||||
copyN(dLabel, offset, src, n) {
|
||||
this.ops.push({op: "COPYN", dLabel, offset, src, n});
|
||||
}
|
||||
|
||||
copyNRet(src, n) {
|
||||
this.ops.push({op: "COPYNRET", src, n});
|
||||
}
|
||||
|
||||
fieldOp(dLabel, fOp, params) {
|
||||
this.ops.push({op: "FOP", dLabel, fOp, params});
|
||||
}
|
||||
|
||||
ret() {
|
||||
this.ops.push({op: "RET"});
|
||||
}
|
||||
|
||||
addLoop(condLabel, body) {
|
||||
this.ops.push({op: "LOOP", condLabel, body});
|
||||
}
|
||||
|
||||
addIf(condLabel, thenCode, elseCode) {
|
||||
this.ops.push({op: "IF", condLabel, thenCode, elseCode});
|
||||
}
|
||||
|
||||
fnCall(fnName, retLabel, params) {
|
||||
this.ops.push({op: "FNCALL", fnName, retLabel, params});
|
||||
}
|
||||
|
||||
checkConstraint(a, b, strErr) {
|
||||
this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
|
||||
}
|
||||
|
||||
|
||||
concat(cb) {
|
||||
this.ops.push(...cb.ops);
|
||||
}
|
||||
|
||||
hasCode() {
|
||||
for (let i=0; i<this.ops.length; i++) {
|
||||
if (this.ops[i].op != "COMMENT") return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_buildOffset(offsets) {
|
||||
let rN=0;
|
||||
let S = "";
|
||||
offsets.forEach((o) => {
|
||||
if ((o[0][0] == "V") && (o[1][0]== "V")) {
|
||||
rN += o[0][1]*o[1][1];
|
||||
return;
|
||||
}
|
||||
let f="";
|
||||
if (o[0][0] == "V") {
|
||||
if (o[0][1]==0) return;
|
||||
f += o[0][1];
|
||||
} else if (o[0][0] == "RI") {
|
||||
if (o[0][1]==0) return;
|
||||
f += o[0][1];
|
||||
} else if (o[0][0] == "R") {
|
||||
f += `Fr_toInt(${o[0][1]})`;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if (o[1][0] == "V") {
|
||||
if (o[1][1]==0) return;
|
||||
if (o[1][1]>1) {
|
||||
f += "*" + o[1][1];
|
||||
}
|
||||
} else if (o[1][0] == "RS") {
|
||||
f += `*${o[1][1]}[${o[1][2]}]`;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if (S!="") S+= " + ";
|
||||
S += f;
|
||||
});
|
||||
if (rN>0) {
|
||||
S = `${rN} + ${S}`;
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
build(code) {
|
||||
this.ops.forEach( (o) => {
|
||||
if (o.op == "COMMENT") {
|
||||
code.push(`/* ${o.comment} */`);
|
||||
} else if (o.op == "BLOCK") {
|
||||
const codeBlock=[];
|
||||
o.block.build(codeBlock);
|
||||
code.push(utils.ident(codeBlock));
|
||||
} else if (o.op == "CALCOFFSETS") {
|
||||
code.push(`${o.dLabel} = ${this._buildOffset(o.offsets)};`);
|
||||
} else if (o.op == "ASSIGN") {
|
||||
const oS = ref2src(o.sOffset);
|
||||
if (oS != "0") {
|
||||
code.push(`${o.dLabel} = ${ref2src(o.src)} + ${oS};`);
|
||||
} else {
|
||||
code.push(`${o.dLabel} = ${ref2src(o.src)};`);
|
||||
}
|
||||
} else if (o.op == "GETSUBCOMPONENTOFFSET") {
|
||||
code.push(`${o.dLabel} = ctx->getSubComponentOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
||||
} else if (o.op == "GETSUBCOMPONENTSIZES") {
|
||||
code.push(`${o.dLabel} = ctx->getSubComponentSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
||||
} else if (o.op == "GETSIGNALOFFSET") {
|
||||
code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
||||
} else if (o.op == "GETSIGNALSIZES") {
|
||||
code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
||||
} else if (o.op == "SETSIGNAL") {
|
||||
code.push(`ctx->setSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${ref2src(o.value)});`);
|
||||
} else if (o.op == "GETSIGNAL") {
|
||||
code.push(`ctx->getSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel});`);
|
||||
} else if (o.op == "COPYN") {
|
||||
const oS = ref2src(o.offset);
|
||||
const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel;
|
||||
code.push(`Fr_copyn(${dLabel}, ${ref2src(o.src)}, ${o.n});`);
|
||||
} else if (o.op == "COPYNRET") {
|
||||
code.push(`Fr_copyn(__retValue, ${ref2src(o.src)}, ${o.n});`);
|
||||
} else if (o.op == "RET") {
|
||||
code.push("goto returnFunc;");
|
||||
} else if (o.op == "FOP") {
|
||||
let paramsS = "";
|
||||
for (let i=0; i<o.params.length; i++) {
|
||||
if (i>0) paramsS += ", ";
|
||||
paramsS += ref2src(o.params[i]);
|
||||
}
|
||||
code.push(`Fr_${o.fOp}(${o.dLabel}, ${paramsS});`);
|
||||
} else if (o.op == "LOOP") {
|
||||
code.push(`while (Fr_isTrue(${o.condLabel})) {`);
|
||||
const body = [];
|
||||
o.body.build(body);
|
||||
code.push(utils.ident(body));
|
||||
code.push("}");
|
||||
} else if (o.op == "IF") {
|
||||
code.push(`if (Fr_isTrue(${o.condLabel})) {`);
|
||||
const thenCode = [];
|
||||
o.thenCode.build(thenCode);
|
||||
code.push(utils.ident(thenCode));
|
||||
if (o.elseCode) {
|
||||
code.push("} else {");
|
||||
const elseCode = [];
|
||||
o.elseCode.build(elseCode);
|
||||
code.push(utils.ident(elseCode));
|
||||
}
|
||||
code.push("}");
|
||||
} else if (o.op == "FNCALL") {
|
||||
code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`);
|
||||
} else if (o.op == "CHECKCONSTRAINT") {
|
||||
code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionBuilderC {
|
||||
|
||||
constructor(name, instanceDef, type) {
|
||||
this.name = name;
|
||||
this.instanceDef = instanceDef;
|
||||
this.type = type; // "COMPONENT" or "FUNCTIOM"
|
||||
this.definedFrElements = [];
|
||||
this.definedIntElements = [];
|
||||
this.definedSizeElements = [];
|
||||
this.definedPFrElements = [];
|
||||
this.initializedElements = [];
|
||||
this.initializedSignalOffset = [];
|
||||
this.initializedSignalSizes = [];
|
||||
|
||||
}
|
||||
|
||||
defineFrElements(dLabel, size) {
|
||||
this.definedFrElements.push({dLabel, size});
|
||||
}
|
||||
|
||||
defineIntElement(dLabel) {
|
||||
this.definedIntElements.push({dLabel});
|
||||
}
|
||||
|
||||
defineSizesElement(dLabel) {
|
||||
this.definedSizeElements.push({dLabel});
|
||||
}
|
||||
|
||||
definePFrElement(dLabel) {
|
||||
this.definedPFrElements.push({dLabel});
|
||||
}
|
||||
|
||||
initializeFrElement(dLabel, offset, idConstant) {
|
||||
this.initializedElements.push({dLabel, offset, idConstant});
|
||||
}
|
||||
|
||||
initializeSignalOffset(dLabel, component, hash, hashLabel) {
|
||||
this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
|
||||
}
|
||||
|
||||
initializeSignalSizes(dLabel, component, hash, hashLabel) {
|
||||
this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
|
||||
}
|
||||
|
||||
setParams(params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
_buildHeader(code) {
|
||||
this.definedFrElements.forEach( (o) => {
|
||||
code.push(`FrElement ${o.dLabel}[${o.size}];`);
|
||||
});
|
||||
this.definedIntElements.forEach( (o) => {
|
||||
code.push(`int ${o.dLabel};`);
|
||||
});
|
||||
this.definedSizeElements.forEach( (o) => {
|
||||
code.push(`Circom_Sizes ${o.dLabel};`);
|
||||
});
|
||||
this.definedPFrElements.forEach( (o) => {
|
||||
code.push(`PFrElement ${o.dLabel};`);
|
||||
});
|
||||
this.initializedElements.forEach( (o) => {
|
||||
code.push(`Fr_copy(&(${o.dLabel}[${o.offset}]), ctx->circuit->constants +${o.idConstant});`);
|
||||
});
|
||||
this.initializedSignalOffset.forEach( (o) => {
|
||||
code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
||||
});
|
||||
this.initializedSignalSizes.forEach( (o) => {
|
||||
code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
||||
});
|
||||
}
|
||||
|
||||
_buildFooter(code) {
|
||||
}
|
||||
|
||||
newCodeBuilder() {
|
||||
return new CodeBuilderC();
|
||||
}
|
||||
|
||||
setBody(body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
build(code) {
|
||||
code.push(
|
||||
"/*",
|
||||
this.instanceDef,
|
||||
"*/"
|
||||
);
|
||||
|
||||
if (this.type=="COMPONENT") {
|
||||
code.push(`void ${this.name}(Circom_CalcWit *ctx, int __cIdx) {`);
|
||||
} else if (this.type=="FUNCTION") {
|
||||
let sParams = "";
|
||||
for (let i=0;i<this.params.length;i++ ) sParams += `, PFrElement ${this.params[i]}`;
|
||||
code.push(`void ${this.name}(Circom_CalcWit *ctx, PFrElement __retValue ${sParams}) {`);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
const fnCode = [];
|
||||
this._buildHeader(fnCode);
|
||||
this.body.build(fnCode);
|
||||
if (this.type=="COMPONENT") {
|
||||
fnCode.push("ctx->finished(__cIdx);");
|
||||
} else if (this.type=="FUNCTION") {
|
||||
fnCode.push("returnFunc: ;");
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
this._buildFooter(fnCode);
|
||||
|
||||
code.push(utils.ident(fnCode));
|
||||
code.push("}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BuilderC {
|
||||
constructor() {
|
||||
this.hashMaps={};
|
||||
this.componentEntriesTables={};
|
||||
this.sizes ={};
|
||||
this.constants = [];
|
||||
this.functions = [];
|
||||
this.components = [];
|
||||
}
|
||||
|
||||
setHeader(header) {
|
||||
this.header=header;
|
||||
}
|
||||
|
||||
// ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
|
||||
addHashMap(name, hm) {
|
||||
this.hashMaps[name] = hm;
|
||||
}
|
||||
|
||||
addComponentEntriesTable(name, cet) {
|
||||
this.componentEntriesTables[name] = cet;
|
||||
}
|
||||
|
||||
addSizes(name, accSizes) {
|
||||
this.sizes[name] = accSizes;
|
||||
}
|
||||
|
||||
addConstant(c) {
|
||||
this.constants.push(c);
|
||||
return this.constants.length - 1;
|
||||
}
|
||||
|
||||
addFunction(fnBuilder) {
|
||||
this.functions.push(fnBuilder);
|
||||
}
|
||||
|
||||
addComponent(component) {
|
||||
this.components.push(component);
|
||||
}
|
||||
|
||||
setMapIsInput(map) {
|
||||
this.mapIsInput = map;
|
||||
}
|
||||
|
||||
setWit2Sig(wit2sig) {
|
||||
this.wit2sig = wit2sig;
|
||||
}
|
||||
|
||||
|
||||
newComponentFunctionBuilder(name, instanceDef) {
|
||||
return new FunctionBuilderC(name, instanceDef, "COMPONENT");
|
||||
}
|
||||
|
||||
newFunctionBuilder(name, instanceDef) {
|
||||
return new FunctionBuilderC(name, instanceDef, "FUNCTION");
|
||||
}
|
||||
|
||||
|
||||
// Body functions
|
||||
|
||||
_buildHeader(code) {
|
||||
code.push(
|
||||
"#include \"circom.h\"",
|
||||
"#include \"calcwit.h\"",
|
||||
`#define NSignals ${this.header.NSignals}`,
|
||||
`#define NComponents ${this.header.NComponents}`,
|
||||
`#define NOutputs ${this.header.NOutputs}`,
|
||||
`#define NInputs ${this.header.NInputs}`,
|
||||
`#define NVars ${this.header.NVars}`,
|
||||
`#define __P__ "${this.header.P.toString()}"`,
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
_buildHashMaps(code) {
|
||||
|
||||
code.push("// Hash Maps ");
|
||||
for (let hmName in this.hashMaps ) {
|
||||
const hm = this.hashMaps[hmName];
|
||||
|
||||
let c = `Circom_HashEntry ${hmName}[256] = {`;
|
||||
for (let i=0; i<256; i++) {
|
||||
c += i>0 ? "," : "";
|
||||
if (hm[i]) {
|
||||
c += `{0x${hm[i][0]}LL, ${hm[i][1]}} /* ${hm[i][2]} */`;
|
||||
} else {
|
||||
c += "{0,0}";
|
||||
}
|
||||
}
|
||||
c += "};";
|
||||
code.push(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_buildComponentEntriesTables(code) {
|
||||
code.push("// Component Entry tables");
|
||||
for (let cetName in this.componentEntriesTables) {
|
||||
const cet = this.componentEntriesTables[cetName];
|
||||
|
||||
code.push(`Circom_ComponentEntry ${cetName}[${cet.length}] = {`);
|
||||
for (let j=0; j<cet.length; j++) {
|
||||
const ty = cet[j].type == "S" ? "_typeSignal" : "_typeComponent";
|
||||
code.push(` ${j>0?",":" "}{${cet[j].offset},${cet[j].sizeName}, ${ty}}`);
|
||||
}
|
||||
code.push("};");
|
||||
}
|
||||
}
|
||||
|
||||
_buildSizes(code) {
|
||||
code.push("// Sizes");
|
||||
for (let sName in this.sizes) {
|
||||
const accSizes = this.sizes[sName];
|
||||
|
||||
let c = `Circom_Size ${sName}[${accSizes.length}] = {`;
|
||||
for (let i=0; i<accSizes.length; i++) {
|
||||
if (i>0) c += ",";
|
||||
c += accSizes[i];
|
||||
}
|
||||
c += "};";
|
||||
code.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
_buildConstants(code) {
|
||||
const self = this;
|
||||
const n64 = Math.floor((self.header.P.bitLength() - 1) / 64)+1;
|
||||
const R = bigInt.one.shiftLeft(n64*64);
|
||||
|
||||
code.push("// Constants");
|
||||
code.push(`FrElement _constants[${self.constants.length}] = {`);
|
||||
for (let i=0; i<self.constants.length; i++) {
|
||||
code.push((i>0 ? "," : " ") + "{" + number2Code(self.constants[i]) + "}");
|
||||
}
|
||||
code.push("};");
|
||||
|
||||
function number2Code(n) {
|
||||
if (n.lt(bigInt("80000000", 16)) ) {
|
||||
return addShortMontgomeryPositive(n);
|
||||
}
|
||||
if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) {
|
||||
return addShortMontgomeryNegative(n);
|
||||
}
|
||||
return addLongMontgomery(n);
|
||||
|
||||
|
||||
function addShortMontgomeryPositive(a) {
|
||||
return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
}
|
||||
|
||||
|
||||
function addShortMontgomeryNegative(a) {
|
||||
const b = a.minus(self.header.P);
|
||||
return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
}
|
||||
|
||||
function addLongMontgomery(a) {
|
||||
return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
}
|
||||
|
||||
function getLongString(a) {
|
||||
let r = bigInt(a);
|
||||
let S = "";
|
||||
let i = 0;
|
||||
while (!r.isZero()) {
|
||||
if (S!= "") S = S+",";
|
||||
S += "0x" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16) + "LL";
|
||||
i++;
|
||||
r = r.shiftRight(64);
|
||||
}
|
||||
while (i<n64) {
|
||||
if (S!= "") S = S+",";
|
||||
S += "0LL";
|
||||
i++;
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
function toMontgomery(a) {
|
||||
return a.times(R).mod(self.header.P);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_buildFunctions(code) {
|
||||
for (let i=0; i<this.functions.length; i++) {
|
||||
const cfb = this.functions[i];
|
||||
cfb.build(code);
|
||||
}
|
||||
}
|
||||
|
||||
_buildComponents(code) {
|
||||
code.push("// Components");
|
||||
code.push(`Circom_Component _components[${this.components.length}] = {`);
|
||||
for (let i=0; i<this.components.length; i++) {
|
||||
const c = this.components[i];
|
||||
const sep = i>0 ? " ," : " ";
|
||||
code.push(`${sep}{${c.hashMapName}, ${c.entryTableName}, ${c.functionName}, ${c.nInSignals}, ${c.newThread}}`);
|
||||
}
|
||||
code.push("};");
|
||||
}
|
||||
|
||||
_buildMapIsInput(code) {
|
||||
code.push("// mapIsInput");
|
||||
code.push(`u32 _mapIsInput[${this.mapIsInput.length}] = {`);
|
||||
let line = "";
|
||||
for (let i=0; i<this.mapIsInput.length; i++) {
|
||||
line += i>0 ? ", " : " ";
|
||||
line += toHex(this.mapIsInput[i]);
|
||||
if (((i+1) % 64)==0) {
|
||||
code.push(" "+line);
|
||||
line = "";
|
||||
}
|
||||
}
|
||||
if (line != "") code.push(" "+line);
|
||||
code.push("};");
|
||||
|
||||
function toHex(number) {
|
||||
if (number < 0) number = 0xFFFFFFFF + number + 1;
|
||||
let S=number.toString(16).toUpperCase();
|
||||
while (S.length<8) S = "0" + S;
|
||||
return "0x"+S;
|
||||
}
|
||||
}
|
||||
|
||||
_buildWit2Sig(code) {
|
||||
code.push("// Witness to Signal Table");
|
||||
code.push(`int _wit2sig[${this.wit2sig.length}] = {`);
|
||||
let line = "";
|
||||
for (let i=0; i<this.wit2sig.length; i++) {
|
||||
line += i>0 ? "," : " ";
|
||||
line += this.wit2sig[i];
|
||||
if (((i+1) % 64) == 0) {
|
||||
code.push(" "+line);
|
||||
line = "";
|
||||
}
|
||||
}
|
||||
if (line != "") code.push(" "+line);
|
||||
code.push("};");
|
||||
}
|
||||
|
||||
_buildCircuitVar(code) {
|
||||
|
||||
code.push(
|
||||
"// Circuit Variable",
|
||||
"Circom_Circuit _circuit = {" ,
|
||||
" NSignals,",
|
||||
" NComponents,",
|
||||
" NInputs,",
|
||||
" NOutputs,",
|
||||
" NVars,",
|
||||
" _wit2sig,",
|
||||
" _components,",
|
||||
" _mapIsInput,",
|
||||
" _constants,",
|
||||
" __P__",
|
||||
"};"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
build() {
|
||||
const code=[];
|
||||
this._buildHeader(code);
|
||||
this._buildSizes(code);
|
||||
this._buildConstants(code);
|
||||
this._buildHashMaps(code);
|
||||
this._buildComponentEntriesTables(code);
|
||||
this._buildFunctions(code);
|
||||
this._buildComponents(code);
|
||||
this._buildMapIsInput(code);
|
||||
this._buildWit2Sig(code);
|
||||
this._buildCircuitVar(code);
|
||||
return streamFromMultiArray(code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = BuilderC;
|
||||
@@ -1,28 +0,0 @@
|
||||
class BuilderWasm {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
setHeader(header) {
|
||||
this.header=header;
|
||||
}
|
||||
|
||||
// ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
|
||||
addHashMap(name, ht) {
|
||||
this.hashTables[name] = ht;
|
||||
}
|
||||
|
||||
addComponentEntriesTable(name, cet) {
|
||||
this.componentEntriesTables[name] = cet;
|
||||
}
|
||||
|
||||
addSizes(name, accSizes) {
|
||||
this.sizes[name] = accSizes;
|
||||
}
|
||||
|
||||
build() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuilderWasm;
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
|
||||
const gen = require("./gencode");
|
||||
|
||||
|
||||
module.exports = function buildWasm(ctx) {
|
||||
|
||||
const fDefined = {};
|
||||
|
||||
ctx.module = new ModuleBuilder();
|
||||
for (let f in ctx.functions) {
|
||||
ctx.f = ctx.module.addFunction(f);
|
||||
ctx.c = ctx.f.getCodeBuilder();
|
||||
|
||||
ctx.scope = {};
|
||||
for (let p in ctx.functions[f].params) {
|
||||
const param = ctx.functions[f].params[p];
|
||||
ctx.f.addParam(param.name, "i32");
|
||||
ctx.scope[param.name] = {
|
||||
type: "PARAM",
|
||||
sels: param.sels,
|
||||
getter: () => { return ctx.c.getLocal(param.name); },
|
||||
setter: (v) => { return ctx.c.setLocal(param.name, v); }
|
||||
};
|
||||
}
|
||||
|
||||
gen(ctx, ctx.functions[f].block);
|
||||
}
|
||||
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const h = hashComponentCall(ctx, i);
|
||||
const fName = ctx.components[i].temlate+"_"+h;
|
||||
if (!fDefined[fName]) {
|
||||
|
||||
ctx.f = ctx.module.addFunction(fName);
|
||||
ctx.c = ctx.f.getCodeBuilder();
|
||||
|
||||
ctx.scope = {};
|
||||
for (let p in ctx.components[i].params) {
|
||||
ctx.scope[p] = createConstant(ctx, ctx.components[i].params[p]);
|
||||
}
|
||||
|
||||
gen(ctx, ctx.templates[ctx.components[i].temlate].block);
|
||||
}
|
||||
ctx.components[i].f = fName;
|
||||
}
|
||||
};
|
||||
|
||||
function buildSetSignal(ctx) {
|
||||
|
||||
|
||||
}
|
||||
186
src/c_tester.js
186
src/c_tester.js
@@ -1,186 +0,0 @@
|
||||
const chai = require("chai");
|
||||
const assert = chai.assert;
|
||||
|
||||
const fs = require("fs");
|
||||
var tmp = require("tmp-promise");
|
||||
const path = require("path");
|
||||
const compiler = require("./compiler");
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
|
||||
const stringifyBigInts = require("./utils").stringifyBigInts;
|
||||
const unstringifyBigInts = require("./utils").unstringifyBigInts;
|
||||
const bigInt = require("big-integer");
|
||||
const utils = require("./utils");
|
||||
const loadR1cs = require("./r1csfile").loadR1cs;
|
||||
const ZqField = require("fflib").ZqField;
|
||||
|
||||
module.exports = c_tester;
|
||||
|
||||
|
||||
async function c_tester(circomFile, _options) {
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
|
||||
|
||||
// console.log(dir.path);
|
||||
|
||||
const baseName = path.basename(circomFile, ".circom");
|
||||
const options = Object.assign({}, _options);
|
||||
|
||||
options.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp"));
|
||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
||||
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
|
||||
await compiler(circomFile, options);
|
||||
|
||||
const cdir = path.join(__dirname, "..", "c");
|
||||
await exec("cp" +
|
||||
` ${path.join(dir.path, baseName + ".cpp")}` +
|
||||
" /tmp/circuit.cpp"
|
||||
);
|
||||
await exec("g++" +
|
||||
` ${path.join(cdir, "main.cpp")}` +
|
||||
` ${path.join(cdir, "calcwit.cpp")}` +
|
||||
` ${path.join(cdir, "utils.cpp")}` +
|
||||
` ${path.join(cdir, "fr.c")}` +
|
||||
` ${path.join(cdir, "fr.o")}` +
|
||||
` ${path.join(dir.path, baseName + ".cpp")} ` +
|
||||
` -o ${path.join(dir.path, baseName)}` +
|
||||
` -I ${cdir}` +
|
||||
" -lgmp -std=c++11 -DSANITY_CHECK"
|
||||
);
|
||||
|
||||
// console.log(dir.path);
|
||||
return new CTester(dir, baseName);
|
||||
}
|
||||
|
||||
class CTester {
|
||||
|
||||
constructor(dir, baseName) {
|
||||
this.dir=dir;
|
||||
this.baseName = baseName;
|
||||
}
|
||||
|
||||
async release() {
|
||||
await this.dir.cleanup();
|
||||
}
|
||||
|
||||
async calculateWitness(input) {
|
||||
await fs.promises.writeFile(
|
||||
path.join(this.dir.path, "in.json"),
|
||||
JSON.stringify(stringifyBigInts(input), null, 1)
|
||||
);
|
||||
await exec(`${path.join(this.dir.path, this.baseName)}` +
|
||||
` ${path.join(this.dir.path, "in.json")}` +
|
||||
` ${path.join(this.dir.path, "out.json")}`
|
||||
);
|
||||
const resStr = await fs.promises.readFile(
|
||||
path.join(this.dir.path, "out.json")
|
||||
);
|
||||
|
||||
const res = unstringifyBigInts(JSON.parse(resStr));
|
||||
return res;
|
||||
}
|
||||
|
||||
async loadSymbols() {
|
||||
if (this.symbols) return;
|
||||
this.symbols = {};
|
||||
const symsStr = await fs.promises.readFile(
|
||||
path.join(this.dir.path, this.baseName + ".sym"),
|
||||
"utf8"
|
||||
);
|
||||
const lines = symsStr.split("\n");
|
||||
for (let i=0; i<lines.length; i++) {
|
||||
const arr = lines[i].split(",");
|
||||
if (arr.length!=3) continue;
|
||||
this.symbols[arr[2]] = {
|
||||
idx: Number(arr[0]),
|
||||
idxWit: Number(arr[1])
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async loadConstraints() {
|
||||
const self = this;
|
||||
if (this.constraints) return;
|
||||
const r1cs = await loadR1cs(path.join(this.dir.path, this.baseName + ".r1cs"),true, false);
|
||||
self.field = new ZqField(r1cs.prime);
|
||||
self.nWires = r1cs.nWires;
|
||||
self.constraints = r1cs.constraints;
|
||||
}
|
||||
|
||||
async assertOut(actualOut, expectedOut) {
|
||||
const self = this;
|
||||
if (!self.symbols) await self.loadSymbols();
|
||||
|
||||
checkObject("main", expectedOut);
|
||||
|
||||
function checkObject(prefix, eOut) {
|
||||
|
||||
if (Array.isArray(eOut)) {
|
||||
for (let i=0; i<eOut.length; i++) {
|
||||
checkObject(prefix + "["+i+"]", eOut[i]);
|
||||
}
|
||||
} else if ((typeof eOut == "object")&&(eOut.constructor.name == "Object")) {
|
||||
for (let k in eOut) {
|
||||
checkObject(prefix + "."+k, eOut[k]);
|
||||
}
|
||||
} else {
|
||||
if (typeof self.symbols[prefix] == "undefined") {
|
||||
assert(false, "Output variable not defined: "+ prefix);
|
||||
}
|
||||
const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString();
|
||||
const be = bigInt(eOut).toString();
|
||||
assert.strictEqual(ba, be, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getDecoratedOutput(witness) {
|
||||
const self = this;
|
||||
const lines = [];
|
||||
if (!self.symbols) await self.loadSymbols();
|
||||
for (let n in self.symbols) {
|
||||
let v;
|
||||
if (utils.isDefined(witness[self.symbols[n].idxWit])) {
|
||||
v = witness[self.symbols[n].idxWit].toString();
|
||||
} else {
|
||||
v = "undefined";
|
||||
}
|
||||
lines.push(`${n} --> ${v}`);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
async checkConstraints(witness) {
|
||||
const self = this;
|
||||
if (!self.constraints) await self.loadConstraints();
|
||||
for (let i=0; i<self.constraints.length; i++) {
|
||||
checkConstraint(self.constraints[i]);
|
||||
}
|
||||
|
||||
function checkConstraint(constraint) {
|
||||
const F = self.field;
|
||||
const a = evalLC(constraint.a);
|
||||
const b = evalLC(constraint.b);
|
||||
const c = evalLC(constraint.c);
|
||||
|
||||
assert (F.sub(F.mul(a,b), c).isZero(), "Constraint doesn't match");
|
||||
}
|
||||
|
||||
function evalLC(lc) {
|
||||
const F = self.field;
|
||||
let v = F.zero;
|
||||
for (let w in lc) {
|
||||
v = F.add(
|
||||
v,
|
||||
F.mul( lc[w], witness[w] )
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
495
src/compiler.js
495
src/compiler.js
@@ -1,399 +1,303 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
||||
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||
|
||||
circom is a free software: you can redistribute it and/or modify it
|
||||
jaz is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
||||
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const sONE = 0;
|
||||
const build = require("./build");
|
||||
const BuilderC = require("./builder_c");
|
||||
const BuilderWasm = require("./builder_wasm");
|
||||
const constructionPhase = require("./construction_phase");
|
||||
const Ctx = require("./ctx");
|
||||
const ZqField = require("fflib").ZqField;
|
||||
const utils = require("./utils");
|
||||
const buildR1cs = require("./r1csfile").buildR1cs;
|
||||
const BigArray = require("./bigarray");
|
||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||
const assert = require("assert");
|
||||
const genCode = require("./gencode");
|
||||
const exec = require("./exec");
|
||||
const lc = require("./lcalgebra");
|
||||
|
||||
const util = require("util");
|
||||
const fs_writeFile = util.promisify(fs.writeFile)
|
||||
|
||||
module.exports = compile;
|
||||
|
||||
async function compile(srcFile, options) {
|
||||
options.p = options.p || __P__;
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (typeof options.reduceConstraints === "undefined") {
|
||||
options.reduceConstraints = true;
|
||||
}
|
||||
const ctx = new Ctx();
|
||||
ctx.field = new ZqField(options.p);
|
||||
ctx.verbose= options.verbose || false;
|
||||
ctx.mainComponent = options.mainComponent || "main";
|
||||
ctx.newThreadTemplates = options.newThreadTemplates;
|
||||
const parser = require("../parser/jaz.js").parser;
|
||||
|
||||
constructionPhase(ctx, srcFile);
|
||||
const timeout = ms => new Promise(res => setTimeout(res, ms))
|
||||
|
||||
console.log("NConstraints Before: "+ctx.constraints.length);
|
||||
async function compile(srcFile, cFile) {
|
||||
const fullFileName = srcFile;
|
||||
const fullFilePath = path.dirname(fullFileName);
|
||||
|
||||
if (ctx.error) {
|
||||
throw(ctx.error);
|
||||
}
|
||||
const src = fs.readFileSync(fullFileName, "utf8");
|
||||
const ast = parser.parse(src);
|
||||
|
||||
if (ctx.getComponentIdx(ctx.mainComponent)<0) {
|
||||
throw new Error("A main component must be defined");
|
||||
}
|
||||
assert(ast.type == "BLOCK");
|
||||
|
||||
const ctx = {
|
||||
scopes: [{}],
|
||||
signals: {
|
||||
one: {
|
||||
fullName: "one",
|
||||
value: bigInt(1),
|
||||
equivalence: "",
|
||||
direction: ""
|
||||
}
|
||||
},
|
||||
currentComponent: "",
|
||||
constraints: [],
|
||||
components: {},
|
||||
templates: {},
|
||||
functions: {},
|
||||
functionParams: {},
|
||||
filePath: fullFilePath,
|
||||
fileName: fullFileName
|
||||
};
|
||||
|
||||
exec(ctx, ast);
|
||||
|
||||
if (ctx.verbose) console.log("Classify Signals");
|
||||
classifySignals(ctx);
|
||||
|
||||
if (ctx.verbose) console.log("Reduce Constants");
|
||||
reduceConstants(ctx);
|
||||
if (options.reduceConstraints) {
|
||||
|
||||
if (ctx.verbose) console.log("Reduce Constraints");
|
||||
// Repeat while reductions are performed
|
||||
let oldNConstrains = -1;
|
||||
while (ctx.constraints.length != oldNConstrains) {
|
||||
console.log("Reducing constraints: "+ctx.constraints.length);
|
||||
oldNConstrains = ctx.constraints.length;
|
||||
reduceConstrains(ctx);
|
||||
}
|
||||
// Repeat while reductions are performed
|
||||
let oldNConstrains = -1;
|
||||
while (ctx.constraints.length != oldNConstrains) {
|
||||
oldNConstrains = ctx.constraints.length;
|
||||
reduceConstrains(ctx);
|
||||
}
|
||||
|
||||
console.log("NConstraints After: "+ctx.constraints.length);
|
||||
|
||||
generateWitnessNames(ctx);
|
||||
|
||||
if (ctx.error) {
|
||||
throw(ctx.error);
|
||||
}
|
||||
|
||||
if (options.cSourceWriteStream) {
|
||||
ctx.builder = new BuilderC();
|
||||
build(ctx);
|
||||
const rdStream = ctx.builder.build();
|
||||
rdStream.pipe(options.cSourceWriteStream);
|
||||
ctx.scopes = [{}];
|
||||
|
||||
// await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill));
|
||||
}
|
||||
|
||||
if (options.wasmWriteStream) {
|
||||
ctx.builder = new BuilderWasm();
|
||||
build(ctx);
|
||||
const rdStream = ctx.builder.build();
|
||||
rdStream.pipe(options.wasmWriteStream);
|
||||
|
||||
// await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill));
|
||||
}
|
||||
|
||||
// const mainCode = gen(ctx,ast);
|
||||
const mainCode = genCode(ctx,ast);
|
||||
if (ctx.error) throw(ctx.error);
|
||||
|
||||
if (options.r1csFileName) {
|
||||
await buildR1cs(ctx, options.r1csFileName);
|
||||
if (cFile) {
|
||||
await fs_writeFile(cFile, mainCode);
|
||||
}
|
||||
|
||||
if (options.symWriteStream) {
|
||||
buildSyms(ctx, options.symWriteStream);
|
||||
}
|
||||
|
||||
// const def = buildCircuitDef(ctx, mainCode);
|
||||
const def = buildCircuitDef(ctx, mainCode);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function classifySignals(ctx) {
|
||||
|
||||
const ERROR = 0xFFFF;
|
||||
|
||||
function priorize(t1, t2) {
|
||||
if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
|
||||
if (t1 == ctx.stINTERNAL) {
|
||||
if ((t1 == "error") || (t2=="error")) return "error";
|
||||
if (t1 == "internal") {
|
||||
return t2;
|
||||
} else if (t2==ctx.stINTERNAL) {
|
||||
} else if (t2=="internal") {
|
||||
return t1;
|
||||
}
|
||||
if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
|
||||
if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
|
||||
if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
|
||||
if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
|
||||
if (t1!=t2) return ERROR;
|
||||
if ((t1 == "one") || (t2 == "one")) return "one";
|
||||
if ((t1 == "constant") || (t2 == "constant")) return "constant";
|
||||
if (t1!=t2) return "error";
|
||||
return t1;
|
||||
}
|
||||
|
||||
// First classify the signals
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let tAll = ctx.stINTERNAL;
|
||||
let tAll = "internal";
|
||||
let lSignal = signal;
|
||||
let end = false;
|
||||
while (!end) {
|
||||
let t = lSignal.c || ctx.stINTERNAL;
|
||||
if (s == 0) {
|
||||
t = ctx.stONE;
|
||||
} else if (lSignal.o & ctx.MAIN) {
|
||||
if (lSignal.o & ctx.IN) {
|
||||
if (lSignal.o & ctx.PRV) {
|
||||
t = ctx.stPRVINPUT;
|
||||
let t = lSignal.category || "internal";
|
||||
if (s == "one") {
|
||||
t = "one";
|
||||
} else if (lSignal.value) {
|
||||
t = "constant";
|
||||
} else if (lSignal.component=="main") {
|
||||
if (lSignal.direction == "IN") {
|
||||
if (lSignal.private) {
|
||||
t = "prvInput";
|
||||
} else {
|
||||
t = ctx.stPUBINPUT;
|
||||
t = "pubInput";
|
||||
}
|
||||
} else if (lSignal.o & ctx.OUT) {
|
||||
t = ctx.stOUTPUT;
|
||||
} else if (lSignal.direction == "OUT") {
|
||||
t = "output";
|
||||
}
|
||||
} else if (utils.isDefined(lSignal.v)) {
|
||||
t = ctx.stCONSTANT;
|
||||
}
|
||||
tAll = priorize(t,tAll);
|
||||
if (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
if (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
} else {
|
||||
end=true;
|
||||
}
|
||||
}
|
||||
if (tAll == ERROR) {
|
||||
if (tAll == "error") {
|
||||
throw new Error("Incompatible types in signal: " + s);
|
||||
}
|
||||
lSignal.c = tAll;
|
||||
lSignal.category = tAll;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function generateWitnessNames(ctx) {
|
||||
const totals = {};
|
||||
totals[ctx.stONE] = 0;
|
||||
totals[ctx.stOUTPUT] = 0;
|
||||
totals[ctx.stPUBINPUT] = 0;
|
||||
totals[ctx.stPRVINPUT] = 0;
|
||||
totals[ctx.stINTERNAL] = 0;
|
||||
totals[ctx.stDISCARDED] = 0;
|
||||
totals[ctx.stCONSTANT] = 0;
|
||||
|
||||
const totals = {
|
||||
"output": 0,
|
||||
"pubInput": 0,
|
||||
"one": 0,
|
||||
"prvInput": 0,
|
||||
"internal": 0,
|
||||
"constant": 0,
|
||||
};
|
||||
const ids = {};
|
||||
|
||||
const counted = {};
|
||||
|
||||
// First classify the signals
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
|
||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
|
||||
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
|
||||
while (lSignal.equivalence) lSignal = ctx.signals[lSignal.equivalence];
|
||||
|
||||
if (!( lSignal.o & ctx.COUNTED) ) {
|
||||
lSignal.o |= ctx.COUNTED;
|
||||
totals[lSignal.c] ++;
|
||||
if (!counted[lSignal.fullName]) {
|
||||
counted[lSignal.fullName] = true;
|
||||
totals[lSignal.category] ++;
|
||||
}
|
||||
}
|
||||
|
||||
ids[ctx.stONE] = 0;
|
||||
ids[ctx.stOUTPUT] = 1;
|
||||
ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
|
||||
ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
|
||||
ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
|
||||
ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
|
||||
ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
|
||||
const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
|
||||
ids["one"] = 0;
|
||||
ids["output"] = 1;
|
||||
ids["pubInput"] = ids["output"] + totals["output"];
|
||||
ids["prvInput"] = ids["pubInput"] + totals["pubInput"];
|
||||
ids["internal"] = ids["prvInput"] + totals["prvInput"];
|
||||
ids["constant"] = ids["internal"] + totals["internal"];
|
||||
const nSignals = ids["constant"] + totals["constant"];
|
||||
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
|
||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
|
||||
ctx.signalNames = new Array(nSignals);
|
||||
for (let i=0; i< nSignals; i++) ctx.signalNames[i] = [];
|
||||
ctx.signalName2Idx = {};
|
||||
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
}
|
||||
if ( typeof(lSignal.id) === "undefined" ) {
|
||||
lSignal.id = ids[lSignal.c] ++;
|
||||
lSignal.id = ids[lSignal.category] ++;
|
||||
}
|
||||
|
||||
signal.id = lSignal.id;
|
||||
ctx.signalNames[signal.id].push(signal.fullName);
|
||||
ctx.signalName2Idx[signal.fullName] = signal.id;
|
||||
}
|
||||
|
||||
ctx.totals = totals;
|
||||
}
|
||||
|
||||
function reduceConstants(ctx) {
|
||||
const newConstraints = new BigArray();
|
||||
const newConstraints = [];
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
|
||||
const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
|
||||
if (!ctx.lc.isZero(c)) {
|
||||
const c = lc.canonize(ctx, ctx.constraints[i]);
|
||||
if (!lc.isZero(c)) {
|
||||
newConstraints.push(c);
|
||||
}
|
||||
delete ctx.constraints[i];
|
||||
}
|
||||
ctx.constraints = newConstraints;
|
||||
}
|
||||
|
||||
function reduceConstrains(ctx) {
|
||||
indexVariables();
|
||||
let possibleConstraints = ctx.constraints;
|
||||
let ii=0;
|
||||
while (possibleConstraints.length>0) {
|
||||
let nextPossibleConstraints = new BigArray();
|
||||
for (let i=0; i<possibleConstraints.length; i++) {
|
||||
ii++;
|
||||
if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
|
||||
if (!ctx.constraints[i]) continue;
|
||||
const c = ctx.constraints[i];
|
||||
|
||||
// Swap a and b if b has more variables.
|
||||
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
||||
const aux = c.a;
|
||||
c.a=c.b;
|
||||
c.b=aux;
|
||||
}
|
||||
|
||||
// Mov to C if possible.
|
||||
if (isConstant(c.a)) {
|
||||
const ct = {t: "N", v: c.a.coefs[sONE]};
|
||||
c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
|
||||
c.a = { t: "LC", coefs: {} };
|
||||
c.b = { t: "LC", coefs: {} };
|
||||
}
|
||||
if (isConstant(c.b)) {
|
||||
const ct = {t: "N", v: c.b.coefs[sONE]};
|
||||
c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
|
||||
c.a = { t: "LC", coefs: {} };
|
||||
c.b = { t: "LC", coefs: {} };
|
||||
}
|
||||
|
||||
if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
|
||||
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
||||
if (isolatedSignal) {
|
||||
|
||||
let lSignal = ctx.signals[isolatedSignal];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
|
||||
|
||||
const isolatedSignalEquivalence = {
|
||||
t: "LC",
|
||||
coefs: {}
|
||||
};
|
||||
const invCoef = c.c.coefs[isolatedSignal].modInv(__P__);
|
||||
for (const s in c.c.coefs) {
|
||||
if (s != isolatedSignal) {
|
||||
const v = __P__.minus(c.c.coefs[s]).times(invCoef).mod(__P__);
|
||||
if (!v.isZero()) {
|
||||
isolatedSignalEquivalence.coefs[s] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let j in lSignal.inConstraints) {
|
||||
if ((j!=i)&&(ctx.constraints[j])) {
|
||||
ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
|
||||
linkSignalsConstraint(j);
|
||||
if (j<i) {
|
||||
nextPossibleConstraints.push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.constraints[i] = null;
|
||||
|
||||
lSignal.c = ctx.stDISCARDED;
|
||||
} else {
|
||||
if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
possibleConstraints = nextPossibleConstraints;
|
||||
}
|
||||
unindexVariables();
|
||||
|
||||
// Pack the constraints
|
||||
let o = 0;
|
||||
const newConstraints = [];
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
if (ctx.constraints[i]) {
|
||||
if (o != i) {
|
||||
ctx.constraints[o] = ctx.constraints[i];
|
||||
const c = ctx.constraints[i];
|
||||
|
||||
// Swap a and b if b has more variables.
|
||||
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
||||
const aux = c.a;
|
||||
c.a=c.b;
|
||||
c.b=aux;
|
||||
}
|
||||
|
||||
// Mov to C if possible.
|
||||
if (isConstant(c.a)) {
|
||||
const ct = {type: "NUMBER", value: c.a.values["one"]};
|
||||
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||
}
|
||||
if (isConstant(c.b)) {
|
||||
const ct = {type: "NUMBER", value: c.b.values["one"]};
|
||||
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||
}
|
||||
|
||||
if (lc.isZero(c.a) || lc.isZero(c.b)) {
|
||||
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
||||
if (isolatedSignal) {
|
||||
const isolatedSignalEquivalence = {
|
||||
type: "LINEARCOMBINATION",
|
||||
values: {}
|
||||
};
|
||||
const invCoef = c.c.values[isolatedSignal].modInv(__P__);
|
||||
for (const s in c.c.values) {
|
||||
if (s != isolatedSignal) {
|
||||
const v = __P__.minus(c.c.values[s]).times(invCoef).mod(__P__);
|
||||
if (!v.isZero()) {
|
||||
isolatedSignalEquivalence.values[s] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let j=0; j<ctx.constraints.length; j++ ) {
|
||||
const c2 = ctx.constraints[j];
|
||||
if (i!=j) {
|
||||
lc.substitute(c2, isolatedSignal, isolatedSignalEquivalence);
|
||||
}
|
||||
}
|
||||
c.a={ type: "LINEARCOMBINATION", values: {} };
|
||||
c.b={ type: "LINEARCOMBINATION", values: {} };
|
||||
c.c={ type: "LINEARCOMBINATION", values: {} };
|
||||
isolatedSignal.category = "constant";
|
||||
}
|
||||
o++;
|
||||
}
|
||||
|
||||
if (!lc.isZero(c)) {
|
||||
newConstraints.push(c);
|
||||
}
|
||||
}
|
||||
ctx.constraints.length = o;
|
||||
|
||||
function indexVariables() {
|
||||
for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
|
||||
}
|
||||
|
||||
function linkSignalsConstraint(cidx) {
|
||||
const ct = ctx.constraints[cidx];
|
||||
for (let k in ct.a.coefs) linkSignal(k, cidx);
|
||||
for (let k in ct.b.coefs) linkSignal(k, cidx);
|
||||
for (let k in ct.c.coefs) linkSignal(k, cidx);
|
||||
}
|
||||
|
||||
function unindexVariables() {
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
let lSignal = ctx.signals[s];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if (lSignal.inConstraints) delete lSignal.inConstraints;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function unlinkSignal(signalName, cidx) {
|
||||
let lSignal = ctx.signals[signalName];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
|
||||
delete lSignal.inConstraints[cidx];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function linkSignal(signalName, cidx) {
|
||||
let lSignal = ctx.signals[signalName];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if (!lSignal.inConstraints) lSignal.inConstraints = {};
|
||||
lSignal.inConstraints[cidx] = true;
|
||||
}
|
||||
ctx.constraints = newConstraints;
|
||||
|
||||
function getFirstInternalSignal(ctx, l) {
|
||||
for (let k in l.coefs) {
|
||||
for (let k in l.values) {
|
||||
const signal = ctx.signals[k];
|
||||
if (signal.c == ctx.stINTERNAL) return k;
|
||||
if (signal.category == "internal") return k;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isConstant(l) {
|
||||
for (let k in l.coefs) {
|
||||
if ((k != sONE) && (!l.coefs[k].isZero())) return false;
|
||||
for (let k in l.values) {
|
||||
if ((k != "one") && (!l.values[k].isZero())) return false;
|
||||
}
|
||||
if (!l.coefs[sONE] || l.coefs[sONE].isZero()) return false;
|
||||
if (!l.values["one"] || l.values["one"].isZero()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
function buildCircuitDef(ctx, mainCode) {
|
||||
const res = {
|
||||
@@ -452,9 +356,6 @@ function buildCircuitDef(ctx, mainCode) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Build constraints
|
||||
|
||||
@@ -476,14 +377,14 @@ is converted to
|
||||
A B C
|
||||
|
||||
*/
|
||||
/*
|
||||
|
||||
function buildConstraints(ctx) {
|
||||
const res = [];
|
||||
|
||||
function fillLC(dst, src) {
|
||||
if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
|
||||
for (let s in src.coefs) {
|
||||
const v = src.coefs[s].toString();
|
||||
if (src.type != "LINEARCOMBINATION") throw new Error("Constraint is not a LINEARCOMBINATION");
|
||||
for (let s in src.values) {
|
||||
const v = src.values[s].toString();
|
||||
const id = ctx.signalName2Idx[s];
|
||||
dst[id] = v;
|
||||
}
|
||||
@@ -496,50 +397,16 @@ function buildConstraints(ctx) {
|
||||
|
||||
fillLC(A, ctx.constraints[i].a);
|
||||
fillLC(B, ctx.constraints[i].b);
|
||||
fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
|
||||
fillLC(C, lc.negate(ctx.constraints[i].c));
|
||||
|
||||
res.push([A,B,C]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
*/
|
||||
function buildSyms(ctx, strm) {
|
||||
|
||||
let nSyms;
|
||||
|
||||
addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
|
||||
|
||||
|
||||
function addSymbolsComponent(prefix, idComponet) {
|
||||
for (let n in ctx.components[idComponet].names.o) {
|
||||
const entrie = ctx.components[idComponet].names.o[n];
|
||||
addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
|
||||
}
|
||||
}
|
||||
|
||||
function addSymbolArray(prefix, type, sizes, offset) {
|
||||
if (sizes.length==0) {
|
||||
if (type == "S") {
|
||||
let s=offset;
|
||||
while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
|
||||
let wId = ctx.signals[s].id;
|
||||
if (typeof(wId) == "undefined") wId=-1;
|
||||
strm.write(`${offset},${wId},${prefix}\n`);
|
||||
nSyms ++;
|
||||
if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
|
||||
} else {
|
||||
addSymbolsComponent(prefix+".", offset);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
let acc = 0;
|
||||
for (let i=0; i<sizes[0]; i++) {
|
||||
acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
function generateCCode(ctx) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
227
src/ctx.js
227
src/ctx.js
@@ -1,227 +0,0 @@
|
||||
const bigInt = require("big-integer");
|
||||
const BigArray = require("./bigarray.js");
|
||||
|
||||
|
||||
class TableName {
|
||||
constructor (ctx) {
|
||||
this.ctx = ctx;
|
||||
this.o = {};
|
||||
}
|
||||
|
||||
_allocElement(name, _sizes, type) {
|
||||
const sizes = _sizes || [];
|
||||
let l = 1;
|
||||
for (let i=0; i<sizes.length; i++) {
|
||||
l = l*sizes[i];
|
||||
}
|
||||
this.o[name] = {
|
||||
sizes: sizes,
|
||||
type: type
|
||||
};
|
||||
return l;
|
||||
}
|
||||
|
||||
addSignal(name, sizes) {
|
||||
const l = this._allocElement(name, sizes, "S");
|
||||
const o = this.ctx.nSignals;
|
||||
this.o[name].offset = o;
|
||||
this.ctx.nSignals += l;
|
||||
if (l>1) {
|
||||
return [o, o+l];
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
addComponent(name, sizes) {
|
||||
const l = this._allocElement(name, sizes, "C");
|
||||
const o = this.ctx.nComponents;
|
||||
this.o[name].offset = o;
|
||||
this.ctx.nComponents += l;
|
||||
if (l>1) {
|
||||
return [o, o+l];
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
_getElement(name, _sels, type) {
|
||||
const sels = _sels || [];
|
||||
const s = this.o[name];
|
||||
if (!s) return -1;
|
||||
if (s.type != type) return -1;
|
||||
if (sels.length > s.sizes.length) return -1;
|
||||
let l=1;
|
||||
for (let i = s.sizes.length-1; i>sels.length; i--) {
|
||||
l = l*s.sizes[i];
|
||||
}
|
||||
let o =0;
|
||||
let p=1;
|
||||
for (let i=sels.length-1; i>=0; i--) {
|
||||
if (sels[i] > s.sizes[i]) return -1; // Out of range
|
||||
if (sels[i] < 0) return -1; // Out of range
|
||||
o += p*sels[i];
|
||||
p *= s.sizes[i];
|
||||
}
|
||||
if (l>1) {
|
||||
return [s.offset + o, s.offset + o + l];
|
||||
} else {
|
||||
return s.offset + o;
|
||||
}
|
||||
}
|
||||
|
||||
getSignalIdx(name, sels) {
|
||||
return this._getElement(name, sels, "S");
|
||||
}
|
||||
|
||||
getComponentIdx(name, sels) {
|
||||
return this._getElement(name, sels, "C");
|
||||
}
|
||||
|
||||
getSizes(name) {
|
||||
return this.o[name].sels;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = class Ctx {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.stONE = 1;
|
||||
this.stOUTPUT = 2;
|
||||
this.stPUBINPUT = 3;
|
||||
this.stPRVINPUT = 4;
|
||||
this.stINTERNAL = 5;
|
||||
this.stDISCARDED = 6;
|
||||
this.stCONSTANT = 7;
|
||||
|
||||
this.IN = 0x01;
|
||||
this.OUT = 0x02;
|
||||
this.PRV = 0x04;
|
||||
this.ONE = 0x08;
|
||||
this.MAIN = 0x10;
|
||||
this.COUNTED = 0x20;
|
||||
|
||||
this.scopes = [{}];
|
||||
this.signals = new BigArray();
|
||||
|
||||
this.currentComponent= -1;
|
||||
this.constraints= new BigArray();
|
||||
this.components= new BigArray();
|
||||
this.templates= {};
|
||||
this.functions= {};
|
||||
this.functionParams= {};
|
||||
this.nSignals = 0;
|
||||
this.nComponents =0;
|
||||
this.names = new TableName(this);
|
||||
this.main=false;
|
||||
this.error = null;
|
||||
this.warnings = [];
|
||||
|
||||
const oneIdx = this.addSignal("one");
|
||||
this.signals[oneIdx] = {
|
||||
v: bigInt(1),
|
||||
o: this.ONE,
|
||||
e: -1,
|
||||
};
|
||||
|
||||
this.uniqueNames = {};
|
||||
}
|
||||
|
||||
addSignal(name, sizes) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.addSignal(name, sizes);
|
||||
} else {
|
||||
return this.names.addSignal(name, sizes);
|
||||
}
|
||||
}
|
||||
|
||||
addComponent(name, sizes) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.addComponent(name, sizes);
|
||||
} else {
|
||||
return this.names.addComponent(name, sizes);
|
||||
}
|
||||
}
|
||||
|
||||
getSignalIdx(name, sels) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.getSignalIdx(name, sels);
|
||||
} else {
|
||||
return this.names.getSignalIdx(name, sels);
|
||||
}
|
||||
}
|
||||
|
||||
getComponentIdx(name, sels) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.getComponentIdx(name, sels);
|
||||
} else {
|
||||
return this.names.getComponentIdx(name, sels);
|
||||
}
|
||||
}
|
||||
|
||||
getSizes(name) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.getSizes(name);
|
||||
} else {
|
||||
return this.names.getSizes(name);
|
||||
}
|
||||
}
|
||||
|
||||
newTableName() {
|
||||
return new TableName(this);
|
||||
}
|
||||
|
||||
_buildErr(ast, errStr) {
|
||||
if (typeof ast == "string") {
|
||||
ast = null;
|
||||
errStr = ast;
|
||||
}
|
||||
if (ast) {
|
||||
return {
|
||||
pos: {
|
||||
first_line: ast.first_line,
|
||||
first_column: ast.first_column,
|
||||
last_line: ast.last_line,
|
||||
last_column: ast.last_column
|
||||
},
|
||||
errStr: errStr,
|
||||
ast: ast,
|
||||
message: errStr,
|
||||
errFile: this.fileName
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
errStr: errStr,
|
||||
message: errStr
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throwError(ast, errStr) {
|
||||
const err = this._buildErr(ast, errStr);
|
||||
this.error = err;
|
||||
}
|
||||
|
||||
logWarning(ast, errStr) {
|
||||
const w = this._buildErr(ast, errStr);
|
||||
this.warnings.push(w);
|
||||
}
|
||||
|
||||
getUniqueName(suggestedName) {
|
||||
if (!suggestedName) {
|
||||
suggestedName = "_tmp";
|
||||
}
|
||||
|
||||
if (typeof(this.uniqueNames[suggestedName]) == "undefined") {
|
||||
this.uniqueNames[suggestedName] = 1;
|
||||
return suggestedName;
|
||||
} else {
|
||||
const name = suggestedName + "_" + this.uniqueNames[suggestedName];
|
||||
this.uniqueNames[suggestedName]++;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
981
src/exec.js
Normal file
981
src/exec.js
Normal file
@@ -0,0 +1,981 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||
|
||||
jaz is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||
|
||||
const lc = require("./lcalgebra");
|
||||
const parser = require("../parser/jaz.js").parser;
|
||||
|
||||
/* TODO: Add lines information
|
||||
|
||||
function setLines(dst, first, last) {
|
||||
last = last || first;
|
||||
dst.first_line = first.first_line;
|
||||
dst.first_column = first.first_column;
|
||||
dst.last_line = last.last_line;
|
||||
dst.last_column = last.last_column;
|
||||
}
|
||||
*/
|
||||
|
||||
module.exports = exec;
|
||||
|
||||
|
||||
function exec(ctx, ast) {
|
||||
if (!ast) {
|
||||
return error(ctx, ast, "Null AST");
|
||||
}
|
||||
if ((ast.type == "NUMBER") || (ast.type == "LINEARCOMBINATION") || (ast.type =="SIGNAL") || (ast.type == "QEQ")) {
|
||||
return ast;
|
||||
} else if (ast.type == "VARIABLE") {
|
||||
return execVariable(ctx, ast);
|
||||
} else if (ast.type == "PIN") {
|
||||
return execPin(ctx, ast);
|
||||
} else if (ast.type == "OP") {
|
||||
if (ast.op == "=") {
|
||||
return execVarAssignement(ctx, ast);
|
||||
} else if (ast.op == "<--") {
|
||||
return execSignalAssign(ctx, ast);
|
||||
} else if (ast.op == "<==") {
|
||||
return execSignalAssignConstrain(ctx, ast);
|
||||
} else if (ast.op == "===") {
|
||||
return execConstrain(ctx, ast);
|
||||
} else if (ast.op == "+=") {
|
||||
return execVarAddAssignement(ctx, ast);
|
||||
} else if (ast.op == "*=") {
|
||||
return execVarMulAssignement(ctx, ast);
|
||||
} else if (ast.op == "+") {
|
||||
return execAdd(ctx, ast);
|
||||
} else if (ast.op == "-") {
|
||||
return execSub(ctx, ast);
|
||||
} else if (ast.op == "UMINUS") {
|
||||
return execUMinus(ctx, ast);
|
||||
} else if (ast.op == "*") {
|
||||
return execMul(ctx, ast);
|
||||
} else if (ast.op == "%") {
|
||||
return execMod(ctx, ast);
|
||||
} else if (ast.op == "PLUSPLUSRIGHT") {
|
||||
return execPlusPlusRight(ctx, ast);
|
||||
} else if (ast.op == "PLUSPLUSLEFT") {
|
||||
return execPlusPlusLeft(ctx, ast);
|
||||
} else if (ast.op == "**") {
|
||||
return execExp(ctx, ast);
|
||||
} else if (ast.op == "&") {
|
||||
return execBAnd(ctx, ast);
|
||||
} else if (ast.op == "<<") {
|
||||
return execShl(ctx, ast);
|
||||
} else if (ast.op == ">>") {
|
||||
return execShr(ctx, ast);
|
||||
} else if (ast.op == "<") {
|
||||
return execLt(ctx, ast);
|
||||
} else if (ast.op == ">") {
|
||||
return execGt(ctx, ast);
|
||||
} else if (ast.op == "<=") {
|
||||
return execLte(ctx, ast);
|
||||
} else if (ast.op == ">=") {
|
||||
return execGte(ctx, ast);
|
||||
} else if (ast.op == "==") {
|
||||
return execEq(ctx, ast);
|
||||
} else if (ast.op == "?") {
|
||||
return execTerCon(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "Invalid operation: " + ast.op);
|
||||
}
|
||||
} else if (ast.type == "DECLARE") {
|
||||
if (ast.declareType == "COMPONENT") {
|
||||
return execDeclareComponent(ctx, ast);
|
||||
} else if ((ast.declareType == "SIGNALIN")||
|
||||
(ast.declareType == "SIGNALOUT")||
|
||||
(ast.declareType == "SIGNAL")) {
|
||||
return execDeclareSignal(ctx, ast);
|
||||
} else if (ast.declareType == "VARIABLE") {
|
||||
return execDeclareVariable(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "Invalid declaration: " + ast.declareType);
|
||||
}
|
||||
} else if (ast.type == "FUNCTIONCALL") {
|
||||
return execFunctionCall(ctx, ast);
|
||||
} else if (ast.type == "BLOCK") {
|
||||
return execBlock(ctx, ast);
|
||||
} else if (ast.type == "FOR") {
|
||||
return execFor(ctx, ast);
|
||||
} else if (ast.type == "WHILE") {
|
||||
return execWhile(ctx, ast);
|
||||
} else if (ast.type == "IF") {
|
||||
return execIf(ctx, ast);
|
||||
} else if (ast.type == "RETURN") {
|
||||
return execReturn(ctx, ast);
|
||||
} else if (ast.type == "TEMPLATEDEF") {
|
||||
return execTemplateDef(ctx, ast);
|
||||
} else if (ast.type == "FUNCTIONDEF") {
|
||||
return execFunctionDef(ctx, ast);
|
||||
} else if (ast.type == "INCLUDE") {
|
||||
return execInclude(ctx, ast);
|
||||
} else if (ast.type == "ARRAY") {
|
||||
return execArray(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "Invalid AST node type: " + ast.type);
|
||||
}
|
||||
}
|
||||
|
||||
function error(ctx, ast, errStr) {
|
||||
ctx.error = {
|
||||
pos: {
|
||||
first_line: ast.first_line,
|
||||
first_column: ast.first_column,
|
||||
last_line: ast.last_line,
|
||||
last_column: ast.last_column
|
||||
},
|
||||
errStr: errStr,
|
||||
errFile: ctx.fileName,
|
||||
ast: ast
|
||||
};
|
||||
}
|
||||
|
||||
function iterateSelectors(ctx, sizes, baseName, fn) {
|
||||
if (sizes.length == 0) {
|
||||
return fn(baseName);
|
||||
}
|
||||
const res = [];
|
||||
for (let i=0; i<sizes[0]; i++) {
|
||||
res.push(iterateSelectors(ctx, sizes.slice(1), baseName+"["+i+"]", fn));
|
||||
if (ctx.error) return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function setScope(ctx, name, selectors, value) {
|
||||
let l = getScopeLevel(ctx, name);
|
||||
if (l==-1) l= ctx.scopes.length-1;
|
||||
|
||||
if (selectors.length == 0) {
|
||||
ctx.scopes[l][name] = value;
|
||||
} else {
|
||||
setScopeArray(ctx.scopes[l][name], selectors);
|
||||
}
|
||||
|
||||
function setScopeArray(a, sels) {
|
||||
if (sels.length == 1) {
|
||||
a[sels[0]] = value;
|
||||
} else {
|
||||
setScopeArray(a[sels[0]], sels.slice(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getScope(ctx, name, selectors) {
|
||||
|
||||
const sels = [];
|
||||
if (selectors) {
|
||||
for (let i=0; i< selectors.length; i++) {
|
||||
const idx = exec(ctx, selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (idx.type != "NUMBER") return error(ctx, selectors[i], "expected a number");
|
||||
sels.push( idx.value.toJSNumber() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function select(v, s) {
|
||||
s = s || [];
|
||||
if (s.length == 0) return v;
|
||||
return select(v[s[0]], s.slice(1));
|
||||
}
|
||||
|
||||
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
||||
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name], sels);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getScopeLevel(ctx, name) {
|
||||
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
||||
if (ctx.scopes[i][name]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function execBlock(ctx, ast) {
|
||||
for (let i=0; i<ast.statements.length; i++) {
|
||||
exec(ctx, ast.statements[i]);
|
||||
if (ctx.returnValue) return;
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
function execTemplateDef(ctx, ast) {
|
||||
const scope = ctx.scopes[0]; // Lets put templates always in top scope.
|
||||
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
if (getScope(ctx, ast.name)) {
|
||||
return error(ctx, ast, "Name already exists: "+ast.name);
|
||||
}
|
||||
scope[ast.name] = {
|
||||
type: "TEMPLATE",
|
||||
params: ast.params,
|
||||
block: ast.block,
|
||||
fileName: ctx.fileName,
|
||||
filePath: ctx.filePath,
|
||||
scopes: copyScope(ctx.scopes)
|
||||
};
|
||||
}
|
||||
|
||||
function execFunctionDef(ctx, ast) {
|
||||
const scope = ctx.scopes[0]; // Lets put functions always in top scope.
|
||||
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
if (getScope(ctx, ast.name)) {
|
||||
return error(ctx, ast, "Name already exists: "+ast.name);
|
||||
}
|
||||
ctx.functionParams[ast.name] = ast.params;
|
||||
scope[ast.name] = {
|
||||
type: "FUNCTION",
|
||||
params: ast.params,
|
||||
block: ast.block,
|
||||
fileName: ctx.fileName,
|
||||
filePath: ctx.filePath,
|
||||
scopes: copyScope(ctx.scopes)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function execDeclareComponent(ctx, ast) {
|
||||
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
|
||||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
|
||||
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||
|
||||
const baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
||||
|
||||
const sizes=[];
|
||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||
const size = exec(ctx, ast.name.selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
}
|
||||
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||
|
||||
ctx.components[fullName] = "UNINSTANTIATED";
|
||||
|
||||
return {
|
||||
type: "COMPONENT",
|
||||
fullName: fullName
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
selectors: []
|
||||
};
|
||||
}
|
||||
|
||||
function execInstantiateComponet(ctx, vr, fn) {
|
||||
|
||||
if (vr.type != "VARIABLE") return error(ctx, fn, "Left hand instatiate component must be a variable");
|
||||
if (fn.type != "FUNCTIONCALL") return error(ctx, fn, "Right type of instantiate component must be a function call");
|
||||
|
||||
const componentName = vr.name;
|
||||
const templateName = fn.name;
|
||||
|
||||
const scopeLevel = getScopeLevel(ctx, templateName);
|
||||
if (scopeLevel == -1) return error(ctx,fn, "Invalid Template");
|
||||
const template = getScope(ctx, templateName);
|
||||
|
||||
if (template.type != "TEMPLATE") return error(ctx, fn, "Invalid Template");
|
||||
|
||||
|
||||
const paramValues = [];
|
||||
for (let i=0; i< fn.params.length; i++) {
|
||||
const v = exec(ctx, fn.params[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (v.type != "NUMBER") return error(ctx, fn.params[i], "expected a number");
|
||||
paramValues.push( v.value);
|
||||
}
|
||||
if (template.params.length != paramValues.length) error(ctx, fn, "Invalid Number of parameters");
|
||||
|
||||
const vv = getScope(ctx, componentName, vr.selectors);
|
||||
|
||||
if (!vv) return error(ctx, vr, "Component not defined"+ componentName);
|
||||
|
||||
instantiateComponent(vv);
|
||||
|
||||
function instantiateComponent(varVal) {
|
||||
if (Array.isArray(varVal)) {
|
||||
for (let i =0; i<varVal.length; i++) {
|
||||
instantiateComponent(varVal[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.components[varVal.fullName] != "UNINSTANTIATED") error(ctx, fn, "Component already instantiated");
|
||||
|
||||
const oldComponent = ctx.currentComponent;
|
||||
const oldFileName = ctx.fileName;
|
||||
const oldFilePath = ctx.filePath;
|
||||
ctx.currentComponent = varVal.fullName;
|
||||
|
||||
ctx.components[ctx.currentComponent] = {
|
||||
signals: [],
|
||||
params: {}
|
||||
};
|
||||
|
||||
const oldScopes = ctx.scopes;
|
||||
|
||||
ctx.scopes = oldScopes.slice(0, scopeLevel+1);
|
||||
|
||||
const scope = {};
|
||||
for (let i=0; i< template.params.length; i++) {
|
||||
scope[template.params[i]] = {
|
||||
type: "NUMBER",
|
||||
value: paramValues[i]
|
||||
};
|
||||
ctx.components[ctx.currentComponent].params[template.params[i]] = paramValues[i];
|
||||
}
|
||||
|
||||
ctx.components[ctx.currentComponent].template = templateName;
|
||||
ctx.fileName = template.fileName;
|
||||
ctx.filePath = template.filePath;
|
||||
ctx.scopes = copyScope( template.scopes );
|
||||
ctx.scopes.push(scope);
|
||||
|
||||
execBlock(ctx, template.block);
|
||||
|
||||
ctx.fileName = oldFileName;
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.currentComponent = oldComponent;
|
||||
ctx.scopes = oldScopes;
|
||||
}
|
||||
}
|
||||
|
||||
function execFunctionCall(ctx, ast) {
|
||||
|
||||
const scopeLevel = getScopeLevel(ctx, ast.name);
|
||||
if (scopeLevel == -1) return error(ctx, ast, "Function not defined: " + ast.name);
|
||||
const fnc = getScope(ctx, ast.name);
|
||||
|
||||
if (fnc.type != "FUNCTION") return error(ctx, ast, "Not a function: " + ast.name);
|
||||
|
||||
const paramValues = [];
|
||||
for (let i=0; i< ast.params.length; i++) {
|
||||
const v = exec(ctx, ast.params[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (v.type != "NUMBER") return error(ctx, ast.params[i], "expected a number");
|
||||
paramValues.push( v.value);
|
||||
}
|
||||
|
||||
if (ast.params.length != paramValues.length) error(ctx, ast, "Invalid Number of parameters");
|
||||
|
||||
const oldFileName = ctx.fileName;
|
||||
const oldFilePath = ctx.filePath;
|
||||
|
||||
const oldScopes = ctx.scopes;
|
||||
|
||||
ctx.scopes = oldScopes.slice(0, scopeLevel+1);
|
||||
|
||||
const scope = {};
|
||||
for (let i=0; i< fnc.params.length; i++) {
|
||||
scope[fnc.params[i]] = {
|
||||
type: "NUMBER",
|
||||
value: paramValues[i]
|
||||
};
|
||||
}
|
||||
|
||||
ctx.fileName = fnc.fileName;
|
||||
ctx.filePath = fnc.filePath;
|
||||
ctx.scopes = copyScope( fnc.scopes );
|
||||
ctx.scopes.push(scope);
|
||||
|
||||
execBlock(ctx, fnc.block);
|
||||
|
||||
const res = ctx.returnValue;
|
||||
ctx.returnValue = null;
|
||||
|
||||
ctx.fileName = oldFileName;
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.scopes = oldScopes;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execReturn(ctx, ast) {
|
||||
ctx.returnValue = exec(ctx, ast.value);
|
||||
return;
|
||||
}
|
||||
|
||||
function execDeclareSignal(ctx, ast) {
|
||||
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
|
||||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
|
||||
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||
|
||||
const baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
||||
|
||||
const sizes=[];
|
||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||
const size = exec(ctx, ast.name.selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
}
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||
ctx.signals[fullName] = {
|
||||
fullName: fullName,
|
||||
direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""),
|
||||
private: ast.private,
|
||||
component: ctx.currentComponent,
|
||||
equivalence: "",
|
||||
alias: [fullName]
|
||||
};
|
||||
ctx.components[ctx.currentComponent].signals.push(fullName);
|
||||
return {
|
||||
type: "SIGNAL",
|
||||
fullName: fullName,
|
||||
};
|
||||
});
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
selectors: []
|
||||
};
|
||||
}
|
||||
|
||||
function execDeclareVariable(ctx, ast) {
|
||||
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
|
||||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid linear combination name");
|
||||
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||
|
||||
const sizes=[];
|
||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||
const size = exec(ctx, ast.name.selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
}
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, "", function() {
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: bigInt(0)
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
selectors: []
|
||||
};
|
||||
}
|
||||
|
||||
function execVariable(ctx, ast) {
|
||||
let v;
|
||||
try {
|
||||
v = getScope(ctx, ast.name, ast.selectors);
|
||||
} catch(err) {
|
||||
console.log(JSON.stringify(ast, null,1));
|
||||
}
|
||||
if (ctx.error) return;
|
||||
|
||||
if (!v) return error(ctx, ast, "Variable not defined");
|
||||
let res;
|
||||
res=v;
|
||||
return res;
|
||||
}
|
||||
|
||||
function execPin(ctx, ast) {
|
||||
const component = getScope(ctx, ast.component.name, ast.component.selectors);
|
||||
if (!component) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
|
||||
if (ctx.error) return;
|
||||
let signalFullName = component.fullName + "." + ast.pin.name;
|
||||
for (let i=0; i< ast.pin.selectors.length; i++) {
|
||||
const sel = exec(ctx, ast.pin.selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||
|
||||
signalFullName += "[" + sel.value.toJSNumber() + "]";
|
||||
}
|
||||
if (!ctx.signals[signalFullName]) error(ctx, ast, "Signal not defined:" + signalFullName);
|
||||
return {
|
||||
type: "SIGNAL",
|
||||
fullName: signalFullName
|
||||
};
|
||||
}
|
||||
|
||||
function execFor(ctx, ast) {
|
||||
exec(ctx, ast.init);
|
||||
if (ctx.error) return;
|
||||
|
||||
let v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
|
||||
while ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||
exec(ctx, ast.body);
|
||||
if (ctx.error) return;
|
||||
|
||||
exec(ctx, ast.step);
|
||||
if (ctx.error) return;
|
||||
|
||||
v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
function execWhile(ctx, ast) {
|
||||
let v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
|
||||
while ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||
exec(ctx, ast.body);
|
||||
if (ctx.error) return;
|
||||
|
||||
v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
function execIf(ctx, ast) {
|
||||
let v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
|
||||
if ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||
exec(ctx, ast.then);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
exec(ctx, ast.else);
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function execVarAssignement(ctx, ast) {
|
||||
let v;
|
||||
if (ast.values[0].type == "DECLARE") {
|
||||
v = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
v = ast.values[0];
|
||||
}
|
||||
const num = getScope(ctx, v.name, v.selectors);
|
||||
if (ctx.error) return;
|
||||
|
||||
if ((typeof(num) != "object")||(num == null)) return error(ctx, ast, "Variable not defined");
|
||||
|
||||
if (num.type == "COMPONENT") return execInstantiateComponet(ctx, v, ast.values[1]);
|
||||
|
||||
const res = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
setScope(ctx, v.name, v.selectors, res);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
function execLt(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.lt(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
function execGt(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.gt(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
function execLte(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.lesserOrEquals(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
function execGte(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.greaterOrEquals(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function execEq(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.eq(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function execBAnd(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.and(b.value).and(__MASK__)
|
||||
};
|
||||
}
|
||||
|
||||
function execShl(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
const v = b.value.greater(256) ? 256 : b.value.value;
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.shiftLeft(v).and(__MASK__)
|
||||
};
|
||||
}
|
||||
|
||||
function execShr(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
const v = b.value.greater(256) ? 256 : b.value.value;
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.shiftRight(v).and(__MASK__)
|
||||
};
|
||||
}
|
||||
|
||||
function execMod(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.mod(b.value)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function execExp(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.modPow(b.value, __P__)
|
||||
};
|
||||
}
|
||||
|
||||
function execAdd(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.add(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execSub(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.sub(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execUMinus(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.negate(a);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execMul(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.mul(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
function execVarAddAssignement(ctx, ast) {
|
||||
const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } );
|
||||
if (ctx.error) return;
|
||||
return execVarAssignement(ctx, { values: [ast.values[0], res] });
|
||||
}
|
||||
|
||||
function execVarMulAssignement(ctx, ast) {
|
||||
const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } );
|
||||
if (ctx.error) return;
|
||||
return execVarAssignement(ctx, { values: [ast.values[0], res] });
|
||||
}
|
||||
|
||||
function execPlusPlusRight(ctx, ast) {
|
||||
const resBefore = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
|
||||
if (ctx.error) return;
|
||||
execVarAssignement(ctx, { values: [ast.values[0], resAfter] });
|
||||
return resBefore;
|
||||
}
|
||||
|
||||
function execPlusPlusLeft(ctx, ast) {
|
||||
if (ctx.error) return;
|
||||
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
|
||||
if (ctx.error) return;
|
||||
execVarAssignement(ctx, { values: [ast.values[0], resAfter] });
|
||||
return resAfter;
|
||||
}
|
||||
|
||||
function execTerCon(ctx, ast) {
|
||||
const cond = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (!cond.value) return { type: "NUMBER" };
|
||||
|
||||
if (cond.value.neq(0)) {
|
||||
return exec(ctx, ast.values[1]);
|
||||
} else {
|
||||
return exec(ctx, ast.values[2]);
|
||||
}
|
||||
}
|
||||
|
||||
function execSignalAssign(ctx, ast) {
|
||||
let vDest;
|
||||
if (ast.values[0].type == "DECLARE") {
|
||||
vDest = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
vDest = ast.values[0];
|
||||
}
|
||||
|
||||
let dst;
|
||||
if (vDest.type == "VARIABLE") {
|
||||
dst = getScope(ctx, vDest.name, vDest.selectors);
|
||||
if (ctx.error) return;
|
||||
} else if (vDest.type == "PIN") {
|
||||
dst = execPin(ctx, vDest);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
error(ctx, ast, "Bad assignement");
|
||||
}
|
||||
|
||||
if (!dst) return error(ctx, ast, "Signal not defined");
|
||||
if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
|
||||
|
||||
let sDest=ctx.signals[dst.fullName];
|
||||
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.fullName);
|
||||
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||
|
||||
if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
|
||||
|
||||
let src = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
|
||||
/*
|
||||
let vSrc;
|
||||
if (ast.values[1].type == "DECLARE") {
|
||||
vSrc = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
vSrc = ast.values[1];
|
||||
}
|
||||
|
||||
if (vSrc.type == "VARIABLE") {
|
||||
src = getScope(ctx, vSrc.name, vSrc.selectors);
|
||||
if (!src) error(ctx, ast, "Variable not defined: " + vSrc.name);
|
||||
if (ctx.error) return;
|
||||
} else if (vSrc.type == "PIN") {
|
||||
src = execPin(ctx, vSrc);
|
||||
}
|
||||
*/
|
||||
|
||||
let assignValue = true;
|
||||
if (src.type == "SIGNAL") {
|
||||
sDest.equivalence = src.fullName;
|
||||
sDest.alias = sDest.alias.concat(src.alias);
|
||||
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||
assignValue = false;
|
||||
}
|
||||
|
||||
if (assignValue) {
|
||||
// const resLC = exec(ctx, vSrc);
|
||||
if (ctx.error) return;
|
||||
|
||||
// const v = lc.evaluate(ctx, resLC);
|
||||
const v = lc.evaluate(ctx, src);
|
||||
|
||||
if (v.value) {
|
||||
sDest.value = v.value;
|
||||
}
|
||||
}
|
||||
|
||||
return vDest;
|
||||
}
|
||||
|
||||
function execConstrain(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.sub(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
if (!lc.isZero(res)) {
|
||||
ctx.constraints.push(lc.toQEQ(res));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execSignalAssignConstrain(ctx, ast) {
|
||||
const v = execSignalAssign(ctx,ast);
|
||||
if (ctx.error) return;
|
||||
execConstrain(ctx, ast);
|
||||
if (ctx.error) return;
|
||||
return v;
|
||||
}
|
||||
|
||||
function execInclude(ctx, ast) {
|
||||
const incFileName = path.resolve(ctx.filePath, ast.file);
|
||||
const incFilePath = path.dirname(incFileName);
|
||||
|
||||
ctx.includedFiles = ctx.includedFiles || [];
|
||||
if (ctx.includedFiles[incFileName]) return;
|
||||
|
||||
ctx.includedFiles[incFileName] = true;
|
||||
|
||||
const src = fs.readFileSync(incFileName, "utf8");
|
||||
|
||||
if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
|
||||
|
||||
const incAst = parser.parse(src);
|
||||
|
||||
const oldFilePath = ctx.filePath;
|
||||
const oldFileName = ctx.fileName;
|
||||
ctx.filePath = incFilePath;
|
||||
ctx.fileName = incFileName;
|
||||
|
||||
exec(ctx, incAst);
|
||||
|
||||
ast.block = incAst;
|
||||
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.fileName = oldFileName;
|
||||
}
|
||||
|
||||
function execArray(ctx, ast) {
|
||||
const res = [];
|
||||
|
||||
for (let i=0; i<ast.values.length; i++) {
|
||||
res.push(exec(ctx, ast.values[i]));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function copyScope(scope) {
|
||||
var scopesClone = [];
|
||||
for (let i=0; i<scope.length; i++) {
|
||||
scopesClone.push(scope[i]);
|
||||
}
|
||||
return scopesClone;
|
||||
}
|
||||
|
||||
1524
src/gencode.js
1524
src/gencode.js
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
|
||||
const assert = require("assert");
|
||||
|
||||
module.exports = iterateAST;
|
||||
|
||||
|
||||
function iterateAST(ast, fn, _pfx) {
|
||||
if (!ast) return;
|
||||
|
||||
const pfx = _pfx || "";
|
||||
let itPfx = 0;
|
||||
|
||||
function getPfx() {
|
||||
res = pfx+"."+itPfx;
|
||||
itPfx ++;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
let res = fn(ast, pfx);
|
||||
if (res) return res;
|
||||
function iterate(arr) {
|
||||
if (arr) {
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
res = iterateAST(arr[i], fn, getPfx());
|
||||
if (res) return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((ast.type == "NUMBER")) {
|
||||
//
|
||||
} else if (ast.type == "VARIABLE") {
|
||||
iterate(ast.selectors);
|
||||
} else if (ast.type == "PIN") {
|
||||
iterate(ast.component.selectors);
|
||||
iterate(ast.pin.selectors);
|
||||
} else if (ast.type == "OP") {
|
||||
iterate(ast.values);
|
||||
} else if (ast.type == "DECLARE") {
|
||||
iterate(ast.name.selectors);
|
||||
} else if (ast.type == "FUNCTIONCALL") {
|
||||
iterate(ast.params);
|
||||
} else if (ast.type == "BLOCK") {
|
||||
iterate(ast.statements);
|
||||
} else if (ast.type == "COMPUTE") {
|
||||
iterateAST(ast.body, fn, getPfx());
|
||||
} else if (ast.type == "FOR") {
|
||||
iterateAST(ast.init, fn, getPfx());
|
||||
iterateAST(ast.condition, fn, getPfx());
|
||||
iterateAST(ast.step, fn, getPfx());
|
||||
iterateAST(ast.body, fn, getPfx());
|
||||
} else if (ast.type == "WHILE") {
|
||||
iterateAST(ast.condition, fn, getPfx());
|
||||
iterateAST(ast.body, fn, getPfx());
|
||||
} else if (ast.type == "IF") {
|
||||
iterateAST(ast.condition, fn, getPfx());
|
||||
iterateAST(ast.then, fn, getPfx());
|
||||
iterateAST(ast.else, fn, getPfx());
|
||||
} else if (ast.type == "RETURN") {
|
||||
iterateAST(ast.value, fn, getPfx());
|
||||
} else if (ast.type == "ARRAY") {
|
||||
iterate(ast.values);
|
||||
} else if ((ast.type == "TEMPLATEDEF")) {
|
||||
//
|
||||
} else if ((ast.type == "FUNCTIONDEF")) {
|
||||
//
|
||||
} else if ((ast.type == "INCLUDE")) {
|
||||
//
|
||||
} else {
|
||||
assert(false, "GEN -> Invalid AST iteration: " + ast.type);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
964
src/lcalgebra.js
964
src/lcalgebra.js
File diff suppressed because it is too large
Load Diff
306
src/r1csfile.js
306
src/r1csfile.js
@@ -1,306 +0,0 @@
|
||||
|
||||
const fs = require("fs");
|
||||
const assert = require("assert");
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
module.exports.buildR1cs = buildR1cs;
|
||||
module.exports.loadR1cs = loadR1cs;
|
||||
|
||||
async function loadR1cs(fileName, loadConstraints, loadMap) {
|
||||
const res = {};
|
||||
const fd = await fs.promises.open(fileName, "r");
|
||||
|
||||
const b = Buffer.allocUnsafe(4);
|
||||
await fd.read(b, 0, 4, 0);
|
||||
|
||||
if (b.toString() != "r1cs") assert(false, "Invalid File format");
|
||||
|
||||
let p=4;
|
||||
|
||||
let v = await readU32();
|
||||
|
||||
if (v>1) assert(false, "Version not supported");
|
||||
|
||||
const nSections = await readU32();
|
||||
|
||||
let pHeader;
|
||||
let pConstraints;
|
||||
let headerSize;
|
||||
let constraintsSize;
|
||||
let pMap;
|
||||
let mapSize;
|
||||
for (let i=0; i<nSections; i++) {
|
||||
let ht = await readU32();
|
||||
let hl = await readDouble64();
|
||||
if (ht == 1) {
|
||||
if (typeof pHeader != "undefined") assert(false, "File has two headder sections");
|
||||
pHeader = p;
|
||||
headerSize = hl;
|
||||
} else if (ht==2) {
|
||||
if (typeof pConstraints != "undefined") assert(false, "File has two constraints sections");
|
||||
pConstraints = p;
|
||||
constraintsSize = hl;
|
||||
} else if (ht==3) {
|
||||
pMap = p;
|
||||
mapSize = hl;
|
||||
}
|
||||
p += hl;
|
||||
}
|
||||
|
||||
if (typeof pHeader == "undefined") assert(false, "File has two header");
|
||||
|
||||
// Read Header
|
||||
p = pHeader;
|
||||
const fieldDefSize = await readU32();
|
||||
const pFieldDef = p;
|
||||
|
||||
const defType = await readU32();
|
||||
if (defType != 1) if (typeof pConstraints != "undefined") assert(false, "Field type not supported");
|
||||
|
||||
res.prime = await readBigInt();
|
||||
|
||||
if ( p != pFieldDef + fieldDefSize) assert("Invalid fieldDef size");
|
||||
|
||||
const bigIntFormat = await readU32();
|
||||
if (bigIntFormat != 0) assert(false, "BigInt format not supported");
|
||||
|
||||
const idSize = await readU32();
|
||||
if (idSize != 4) assert(false, "idSize not supported. Mus be 4");
|
||||
|
||||
res.nWires = await readU32();
|
||||
res.nPubOuts = await readU32();
|
||||
res.nPubIns = await readU32();
|
||||
res.nPrvIns = await readU32();
|
||||
res.nLabels = await readU32();
|
||||
res.nConstraints = await readU32();
|
||||
|
||||
if (p != pHeader + headerSize) assert(false, "Invalid header section size");
|
||||
|
||||
if (loadConstraints) {
|
||||
// Read Constraints
|
||||
p = pConstraints;
|
||||
|
||||
res.constraints = [];
|
||||
for (let i=0; i<res.nConstraints; i++) {
|
||||
const c = await readConstraint();
|
||||
res.constraints.push(c);
|
||||
}
|
||||
if (p != pConstraints + constraintsSize) assert(false, "Invalid constraints size");
|
||||
}
|
||||
|
||||
// Read Labels
|
||||
|
||||
if (loadMap) {
|
||||
p = pMap;
|
||||
|
||||
res.map = [];
|
||||
for (let i=0; i<res.nLabels; i++) {
|
||||
const idx = await readU32();
|
||||
res.map.push(idx);
|
||||
}
|
||||
if (p != pMap + mapSize) assert(false, "Invalid Map size");
|
||||
}
|
||||
|
||||
await fd.close();
|
||||
|
||||
return res;
|
||||
|
||||
async function readU32() {
|
||||
const b = Buffer.allocUnsafe(4);
|
||||
await fd.read(b, 0, 4, p);
|
||||
|
||||
p+=4;
|
||||
|
||||
return b.readInt32LE(0);
|
||||
}
|
||||
|
||||
async function readDouble64() {
|
||||
const b = Buffer.allocUnsafe(8);
|
||||
await fd.read(b, 0, 8, p);
|
||||
|
||||
p+=8;
|
||||
|
||||
return b.readDoubleLE(0);
|
||||
}
|
||||
|
||||
async function readBigInt() {
|
||||
const bl = Buffer.allocUnsafe(1);
|
||||
await fd.read(bl, 0, 1, p);
|
||||
p++;
|
||||
|
||||
const l = bl[0];
|
||||
const b = Buffer.allocUnsafe(l);
|
||||
await fd.read(b, 0, l, p);
|
||||
p += l;
|
||||
|
||||
const arr = Uint8Array.from(b);
|
||||
|
||||
const arrr = new Array(arr.length);
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
arrr[i] = arr[arr.length-1-i];
|
||||
}
|
||||
|
||||
const n = bigInt.fromArray(arrr, 256);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
async function readConstraint() {
|
||||
const c = {};
|
||||
c.a = await readLC();
|
||||
c.b = await readLC();
|
||||
c.c = await readLC();
|
||||
return c;
|
||||
}
|
||||
|
||||
async function readLC() {
|
||||
const lc= {};
|
||||
const nIdx = await readU32();
|
||||
for (let i=0; i<nIdx; i++) {
|
||||
const idx = await readU32();
|
||||
const val = await readBigInt();
|
||||
lc[idx] = val;
|
||||
}
|
||||
return lc;
|
||||
}
|
||||
}
|
||||
|
||||
async function buildR1cs(ctx, fileName) {
|
||||
|
||||
const fd = await fs.promises.open(fileName, "w");
|
||||
|
||||
|
||||
await fd.write("r1cs"); // Magic "r1cs"
|
||||
|
||||
let p = 4;
|
||||
await writeU32(1); // Version
|
||||
await writeU32(3); // Number of Sections
|
||||
|
||||
// Write the header
|
||||
///////////
|
||||
await writeU32(1); // Header type
|
||||
const pHeaderSize = p;
|
||||
await writeDouble64(0); // Temporally set to 0 length
|
||||
|
||||
|
||||
// Field Def
|
||||
const pFieldDefSize = p;
|
||||
await writeU32(0); // Temporally set to 0 length
|
||||
await writeU32(1);
|
||||
await writeBigInt(ctx.field.p);
|
||||
const fieldDefSize = p - pFieldDefSize - 4;
|
||||
|
||||
await writeU32(0); // Variable bigInt format
|
||||
await writeU32(4); // Id Size
|
||||
|
||||
const NWires =
|
||||
ctx.totals[ctx.stONE] +
|
||||
ctx.totals[ctx.stOUTPUT] +
|
||||
ctx.totals[ctx.stPUBINPUT] +
|
||||
ctx.totals[ctx.stPRVINPUT] +
|
||||
ctx.totals[ctx.stINTERNAL];
|
||||
|
||||
await writeU32(NWires);
|
||||
await writeU32(ctx.totals[ctx.stOUTPUT]);
|
||||
await writeU32(ctx.totals[ctx.stPUBINPUT]);
|
||||
await writeU32(ctx.totals[ctx.stPRVINPUT]);
|
||||
await writeU32(ctx.signals.length);
|
||||
await writeU32(ctx.constraints.length);
|
||||
|
||||
const headerSize = p - pHeaderSize - 8;
|
||||
|
||||
// Write constraints
|
||||
///////////
|
||||
await writeU32(2); // Constraints type
|
||||
const pConstraintsSize = p;
|
||||
await writeDouble64(0); // Temporally set to 0 length
|
||||
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
||||
if (ctx.verbose) console.log("writing constraint: ", i);
|
||||
await fd.datasync();
|
||||
}
|
||||
await writeConstraint(ctx.constraints[i]);
|
||||
}
|
||||
|
||||
const constraintsSize = p - pConstraintsSize - 8;
|
||||
|
||||
// Write map
|
||||
///////////
|
||||
await writeU32(3); // wires2label type
|
||||
const pMapSize = p;
|
||||
await writeDouble64(0); // Temporally set to 0 length
|
||||
|
||||
|
||||
const arr = new Array(NWires);
|
||||
for (let i=0; i<ctx.signals.length; i++) {
|
||||
const outIdx = ctx.signals[i].id;
|
||||
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
||||
assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
|
||||
if (outIdx>=NWires) continue; // Is a constant or a discarded variable
|
||||
if (typeof arr[ctx.signals[i].id] == "undefined") {
|
||||
arr[outIdx] = i;
|
||||
}
|
||||
}
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
await writeU32(arr[i]);
|
||||
if ((ctx.verbose)&&(i%100000)) console.log("writing label2wire map: ", i);
|
||||
}
|
||||
|
||||
const mapSize = p - pMapSize - 8;
|
||||
|
||||
// Write sizes
|
||||
await writeDouble64(headerSize, pHeaderSize);
|
||||
await writeU32(fieldDefSize, pFieldDefSize);
|
||||
await writeDouble64(constraintsSize, pConstraintsSize);
|
||||
await writeDouble64(mapSize, pMapSize);
|
||||
|
||||
await fd.sync();
|
||||
await fd.close();
|
||||
|
||||
async function writeU32(v, pos) {
|
||||
const b = Buffer.allocUnsafe(4);
|
||||
b.writeInt32LE(v);
|
||||
|
||||
await fd.write(b, 0, 4, pos);
|
||||
|
||||
if (typeof(pos) == "undefined") p += 4;
|
||||
}
|
||||
|
||||
async function writeDouble64(v, pos) {
|
||||
const b = Buffer.allocUnsafe(8);
|
||||
b.writeDoubleLE(v);
|
||||
|
||||
await fd.write(b, 0, 8, pos);
|
||||
|
||||
if (typeof(pos) == "undefined") p += 8;
|
||||
}
|
||||
|
||||
async function writeConstraint(c) {
|
||||
await writeLC(c.a);
|
||||
await writeLC(c.b);
|
||||
await writeLC(ctx.lc.neg(c.c));
|
||||
}
|
||||
|
||||
async function writeLC(lc) {
|
||||
const idxs = Object.keys(lc.coefs);
|
||||
await writeU32(idxs.length);
|
||||
for (let s in lc.coefs) {
|
||||
let lSignal = ctx.signals[s];
|
||||
|
||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||
|
||||
await writeU32(lSignal.id);
|
||||
await writeBigInt(lc.coefs[s]);
|
||||
}
|
||||
}
|
||||
|
||||
async function writeBigInt(n) {
|
||||
|
||||
const bytes = bigInt(n).toArray(256).value.reverse();
|
||||
|
||||
await fd.write(Buffer.from([bytes.length, ...bytes ]));
|
||||
|
||||
p += bytes.length+1;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
const Readable = require("stream").Readable;
|
||||
|
||||
module.exports = function streamFromMultiarray(ma) {
|
||||
const rs = Readable();
|
||||
|
||||
let curIndex = getFirstIdx(ma);
|
||||
|
||||
rs._read = function() {
|
||||
let res;
|
||||
res = objFromIdx(ma, curIndex);
|
||||
curIndex = nextIdx(curIndex);
|
||||
if (res!=null) {
|
||||
rs.push(res + "\n");
|
||||
} else {
|
||||
rs.push(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return rs;
|
||||
|
||||
|
||||
function getFirstIdx(ma) {
|
||||
if (!Array.isArray(ma)) return [];
|
||||
return [0, ...getFirstIdx(ma[0])];
|
||||
}
|
||||
|
||||
function nextIdx(idx) {
|
||||
if (idx == null) return null;
|
||||
if (idx.length == 0) return null;
|
||||
|
||||
const parentIdx = idx.slice(0,-1);
|
||||
|
||||
const itObj = objFromIdx(ma, parentIdx);
|
||||
const newLastIdx = idx[idx.length-1]+1;
|
||||
if (newLastIdx < itObj.length) {
|
||||
const resIdx = idx.slice();
|
||||
resIdx[resIdx.length-1] = newLastIdx;
|
||||
return [...resIdx, ...getFirstIdx(itObj[newLastIdx])];
|
||||
} else {
|
||||
return nextIdx(parentIdx);
|
||||
}
|
||||
}
|
||||
|
||||
function objFromIdx(ma, idx) {
|
||||
if (idx == null) return null;
|
||||
if (idx.length == 0) return ma;
|
||||
if (ma.length == 0) return "";
|
||||
return objFromIdx(ma[idx[0]], idx.slice(1));
|
||||
}
|
||||
};
|
||||
134
src/utils.js
134
src/utils.js
@@ -1,134 +0,0 @@
|
||||
const fnv = require("fnv-plus");
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
module.exports.ident =ident;
|
||||
|
||||
module.exports.extractSizes =extractSizes;
|
||||
module.exports.flatArray = flatArray;
|
||||
module.exports.csArr = csArr;
|
||||
module.exports.accSizes = accSizes;
|
||||
module.exports.fnvHash = fnvHash;
|
||||
module.exports.stringifyBigInts = stringifyBigInts;
|
||||
module.exports.unstringifyBigInts = unstringifyBigInts;
|
||||
module.exports.sameSizes = sameSizes;
|
||||
module.exports.isDefined = isDefined;
|
||||
module.exports.accSizes2Str = accSizes2Str;
|
||||
|
||||
function ident(text) {
|
||||
if (typeof text === "string") {
|
||||
let lines = text.split("\n");
|
||||
for (let i=0; i<lines.length; i++) {
|
||||
if (lines[i]) lines[i] = " "+lines[i];
|
||||
}
|
||||
return lines.join("\n");
|
||||
} else if (Array.isArray(text)) {
|
||||
for (let i=0; i<text.length; i++ ) {
|
||||
text[i] = ident(text[i]);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
function extractSizes (o) {
|
||||
if (! Array.isArray(o)) return [];
|
||||
return [o.length, ...extractSizes(o[0])];
|
||||
}
|
||||
|
||||
function flatArray(a) {
|
||||
var res = [];
|
||||
fillArray(res, a);
|
||||
return res;
|
||||
|
||||
function fillArray(res, a) {
|
||||
if (Array.isArray(a)) {
|
||||
for (let i=0; i<a.length; i++) {
|
||||
fillArray(res, a[i]);
|
||||
}
|
||||
} else {
|
||||
res.push(bigInt(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input [1,2,3]
|
||||
// Returns " ,1 ,2, 3"
|
||||
function csArr(_arr) {
|
||||
let S = "";
|
||||
const arr = _arr || [];
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
S = " ,"+arr[i];
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
function accSizes(_sizes) {
|
||||
const sizes = _sizes || [];
|
||||
const accSizes = [1, 0];
|
||||
for (let i=sizes.length-1; i>=0; i--) {
|
||||
accSizes.unshift(accSizes[0]*sizes[i]);
|
||||
}
|
||||
return accSizes;
|
||||
}
|
||||
|
||||
function fnvHash(str) {
|
||||
return fnv.hash(str, 64).hex();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function stringifyBigInts(o) {
|
||||
if ((typeof(o) == "bigint") || o.isZero !== undefined) {
|
||||
return o.toString(10);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(stringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
res[k] = stringifyBigInts(o[k]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function unstringifyBigInts(o) {
|
||||
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
|
||||
return bigInt(o);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(unstringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
res[k] = unstringifyBigInts(o[k]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return bigInt(o);
|
||||
}
|
||||
}
|
||||
|
||||
function sameSizes(s1, s2) {
|
||||
if (!Array.isArray(s1)) return false;
|
||||
if (!Array.isArray(s2)) return false;
|
||||
if (s1.length != s2.length) return false;
|
||||
for (let i=0; i<s1.length; i++) {
|
||||
if (s1[i] != s2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isDefined(v) {
|
||||
return ((typeof(v) != "undefined")&&(v != null));
|
||||
}
|
||||
|
||||
function accSizes2Str(sizes) {
|
||||
if (sizes.length == 2) return "";
|
||||
return `[${sizes[0]/sizes[1]}]`+accSizes2Str(sizes.slice(1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
const path = require("path");
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const c_tester = require("../index.js").c_tester;
|
||||
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
|
||||
function normalize(o) {
|
||||
if ((typeof(o) == "bigint") || o.isZero !== undefined) {
|
||||
const res = bigInt(o);
|
||||
return norm(res);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(normalize);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
res[k] = normalize(o[k]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
const res = bigInt(o);
|
||||
return norm(res);
|
||||
}
|
||||
|
||||
function norm(n) {
|
||||
let res = n.mod(__P__);
|
||||
if (res.isNegative()) res = __P__.add(res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function doTest(circuit, testVectors) {
|
||||
const cir = await c_tester(path.join(__dirname, "circuits", circuit));
|
||||
|
||||
for (let i=0; i<testVectors.length; i++) {
|
||||
const w = await cir.calculateWitness(normalize(testVectors[i][0]));
|
||||
await cir.assertOut(w, normalize(testVectors[i][1]) );
|
||||
}
|
||||
|
||||
await cir.release();
|
||||
}
|
||||
|
||||
describe("basic cases", function () {
|
||||
this.timeout(100000);
|
||||
|
||||
it("inout", async () => {
|
||||
await doTest(
|
||||
"inout.circom",
|
||||
[
|
||||
[{in1: 1, in2: [2,3], in3:[[4,5], [6,7], [8,9]]}, {out1: 1, out2: [2,3], out3: [[4,5], [6,7],[8,9]]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("add", async () => {
|
||||
await doTest(
|
||||
"add.circom",
|
||||
[
|
||||
[{in: [0,0]}, {out: 0}],
|
||||
[{in: [0,1]}, {out: 1}],
|
||||
[{in: [1,2]}, {out: 3}],
|
||||
[{in: [__P__.minus(1),1]}, {out: 0}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("add constant", async () => {
|
||||
await doTest(
|
||||
"addconst1.circom",
|
||||
[
|
||||
[{in: 0}, {out: 15}],
|
||||
[{in: 10}, {out: 25}],
|
||||
[{in: __P__.minus(2)}, {out: 13}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("for unrolled", async () => {
|
||||
await doTest(
|
||||
"forunrolled.circom",
|
||||
[
|
||||
[{in: 0}, {out: [0,1,2]}],
|
||||
[{in: 10}, {out: [10, 11, 12]}],
|
||||
[{in: __P__.minus(2)}, {out: [__P__.minus(2), __P__.minus(1), 0]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("for rolled", async () => {
|
||||
await doTest(
|
||||
"forrolled.circom",
|
||||
[
|
||||
[{in: 0}, {out: 0}],
|
||||
[{in: 10}, {out: 10}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("while unrolled", async () => {
|
||||
await doTest(
|
||||
"whileunrolled.circom",
|
||||
[
|
||||
[{in: 0}, {out: [0,1,2]}],
|
||||
[{in: 10}, {out: [10, 11, 12]}],
|
||||
[{in: __P__.minus(2)}, {out: [__P__.minus(2), __P__.minus(1), 0]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("while rolled", async () => {
|
||||
await doTest(
|
||||
"whilerolled.circom",
|
||||
[
|
||||
[{in: 0}, {out: 0}],
|
||||
[{in: 10}, {out: 10}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("function1", async () => {
|
||||
await doTest(
|
||||
"function1.circom",
|
||||
[
|
||||
[{in: 0}, {out: 3}],
|
||||
[{in: 10}, {out: 13}],
|
||||
[{in: __P__.minus(2)}, {out: 1}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("function2", async () => {
|
||||
await doTest(
|
||||
"function2.circom",
|
||||
[
|
||||
[{in: 0}, {out: 3}],
|
||||
[{in: 10}, {out: 13}],
|
||||
[{in: __P__.minus(2)}, {out: 1}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("constants1", async () => {
|
||||
await doTest(
|
||||
"constants1.circom",
|
||||
[
|
||||
[{in: 0}, {out: 42}],
|
||||
[{in: 10}, {out: 52}],
|
||||
[{in: __P__.minus(2)}, {out: 40}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("arrays", async () => {
|
||||
await doTest(
|
||||
"arrays.circom",
|
||||
[
|
||||
[{in: 0}, {out: [1, 8, 51]}],
|
||||
[{in: 10}, {out: [11, 28, 111]}],
|
||||
[{in: __P__.minus(2)}, {out: [__P__.minus(1), 4, 39]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("if unrolled", async () => {
|
||||
await doTest(
|
||||
"ifunrolled.circom",
|
||||
[
|
||||
[{in: 0}, {out: [1, 3, 6]}],
|
||||
[{in: 10}, {out: [11, 13, 16]}],
|
||||
[{in: __P__.minus(2)}, {out: [__P__.minus(1), 1, 4]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("if rolled", async () => {
|
||||
await doTest(
|
||||
"ifrolled.circom",
|
||||
[
|
||||
[{in: 0}, {out: [1, 0, 0]}],
|
||||
[{in: 1}, {out: [0, 1, 0]}],
|
||||
[{in: 2}, {out: [0, 0, 1]}],
|
||||
[{in: 3}, {out: [0, 0, 0]}],
|
||||
[{in: __P__.minus(2)}, {out: [0,0,0]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("inc", async () => {
|
||||
await doTest(
|
||||
"inc.circom",
|
||||
[
|
||||
[{in: 0}, {out: [5, 2]}],
|
||||
[{in: 1}, {out: [6, 4]}],
|
||||
[{in: 2}, {out: [7, 6]}],
|
||||
[{in: 3}, {out: [8, 8]}],
|
||||
[{in: __P__.minus(2)}, {out: [3,__P__.minus(2)]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("dec", async () => {
|
||||
await doTest(
|
||||
"dec.circom",
|
||||
[
|
||||
[{in: 0}, {out: [1, __P__.minus(2)]}],
|
||||
[{in: 1}, {out: [2, 0]}],
|
||||
[{in: 2}, {out: [3, 2]}],
|
||||
[{in: 3}, {out: [4, 4]}],
|
||||
[{in: __P__.minus(2)}, {out: [__P__.minus(1),__P__.minus(6)]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("ops", async () => {
|
||||
await doTest(
|
||||
"ops.circom",
|
||||
[
|
||||
[{in: [-2, 2]}, {add: 0, sub: -4, mul: -4}],
|
||||
[{in: [-1, 1]}, {add: 0, sub: -2, mul: -1}],
|
||||
[{in: [ 0, 0]}, {add: 0, sub: 0, mul: 0}],
|
||||
[{in: [ 1,-1]}, {add: 0, sub: 2, mul: -1}],
|
||||
[{in: [ 2,-2]}, {add: 0, sub: 4, mul: -4}],
|
||||
[{in: [-2,-3]}, {add: -5, sub: 1, mul: 6}],
|
||||
[{in: [ 2, 3]}, {add: 5, sub: -1, mul: 6}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("ops2", async () => {
|
||||
await doTest(
|
||||
"ops2.circom",
|
||||
[
|
||||
[{in: [-2, 2]}, {div: -1, idiv: bigInt("10944121435919637611123202872628637544274182200208017171849102093287904247807"), mod: 1}],
|
||||
[{in: [-1, 1]}, {div: -1, idiv: -1, mod: 0}],
|
||||
[{in: [ 1,-1]}, {div: -1, idiv: 0, mod: 1}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("ops3", async () => {
|
||||
await doTest(
|
||||
"ops3.circom",
|
||||
[
|
||||
[{in: [-2, 2]}, {neg1: 2,neg2: -2, pow: 4}],
|
||||
[{in: [0, 1]}, {neg1: 0, neg2: -1, pow: 0}],
|
||||
[{in: [ 1,-1]}, {neg1: -1, neg2: 1, pow: 1}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Comparation ops", async () => {
|
||||
await doTest(
|
||||
"opscmp.circom",
|
||||
[
|
||||
[{in: [ 8, 9]}, {lt: 1, leq: 1, eq:0, neq:1, geq: 0, gt:0}],
|
||||
[{in: [-2,-2]}, {lt: 0, leq: 1, eq:1, neq:0, geq: 1, gt:0}],
|
||||
[{in: [-1,-2]}, {lt: 0, leq: 0, eq:0, neq:1, geq: 1, gt:1}],
|
||||
[{in: [ 1,-1]}, {lt: 0, leq: 0, eq:0, neq:1, geq: 1, gt:1}], // In mod, negative values are higher than positive.
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Bit ops", async () => {
|
||||
const mask = bigInt("14474011154664524427946373126085988481658748083205070504932198000989141204991");
|
||||
const m1m = bigInt("7414231717174750794300032619171286606889616317210963838766006185586667290624");
|
||||
await doTest(
|
||||
"opsbit.circom",
|
||||
[
|
||||
[{in: [ 5, 3]}, {and: 1, or: 7, xor:6, not1:mask.minus(5), shl: 40, shr:0}],
|
||||
[{in: [ 0, 0]}, {and: 0, or: 0, xor:0, not1:mask, shl: 0, shr:0}],
|
||||
[{in: [-1, 1]}, {and: 0, or: m1m.add(bigInt.one), xor:m1m.add(bigInt.one), not1:mask.minus(m1m), shl: m1m.shiftLeft(1).and(mask), shr:__P__.shiftRight(1).and(mask)}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Logical ops", async () => {
|
||||
await doTest(
|
||||
"opslog.circom",
|
||||
[
|
||||
[{in: [ 5, 0]}, {and: 0, or: 1, not1:0}],
|
||||
[{in: [ 0, 1]}, {and: 0, or: 1, not1:1}],
|
||||
[{in: [-1, 9]}, {and: 1, or: 1, not1:0}],
|
||||
[{in: [ 0, 0]}, {and: 0, or: 0, not1:1}],
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it("Conditional Ternary operator", async () => {
|
||||
await doTest(
|
||||
"condternary.circom",
|
||||
[
|
||||
[{in: 0}, {out: 21}],
|
||||
[{in: 1}, {out: 1}],
|
||||
[{in: 2}, {out: 23}],
|
||||
[{in:-1}, {out: 20}],
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it("Compute block", async () => {
|
||||
await doTest(
|
||||
"compute.circom",
|
||||
[
|
||||
[{x: 1}, {y: 7}],
|
||||
[{x: 2}, {y: 7}],
|
||||
[{x: 3}, {y: 11}],
|
||||
[{x:-1}, {y: -5}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Component array ", async () => {
|
||||
await doTest(
|
||||
"componentarray.circom",
|
||||
[
|
||||
[{in: 1}, {out: 1}],
|
||||
[{in: 2}, {out: 256}],
|
||||
[{in: 3}, {out: 6561}],
|
||||
[{in:-1}, {out: 1}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Component array 2d", async () => {
|
||||
await doTest(
|
||||
"componentarray2.circom",
|
||||
[
|
||||
[{in: [1,2]}, {out: [1, 256]}],
|
||||
[{in: [0,3]}, {out: [0, 6561]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Constant circuit", async () => {
|
||||
await doTest(
|
||||
"constantcircuit.circom",
|
||||
[
|
||||
// 0xbb67ae85
|
||||
[{}, {out: [1,0,1,0, 0,0,0,1, 0,1,1,1, 0,1,0,1, 1,1,1,0, 0,1,1,0, 1,1,0,1, 1,1,0,1]}],
|
||||
]
|
||||
);
|
||||
});
|
||||
it("Constant internal circuit", async () => {
|
||||
await doTest(
|
||||
"constantinternalcircuit.circom",
|
||||
[
|
||||
[{in: 1}, {out: 5}],
|
||||
[{in: 0}, {out: 4}],
|
||||
[{in: -2}, {out: 2}],
|
||||
[{in: 10}, {out: 14}]
|
||||
]
|
||||
);
|
||||
});
|
||||
it("include", async () => {
|
||||
await doTest(
|
||||
"include.circom",
|
||||
[
|
||||
[{in: 3}, {out: 6}],
|
||||
[{in: 6}, {out: 15}],
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
const chai = require("chai");
|
||||
const path = require("path");
|
||||
const snarkjs = require("snarkjs");
|
||||
|
||||
const bigInt = snarkjs.bigInt;
|
||||
|
||||
const compiler = require("../index.js");
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
async function assertThrowsAsync(fn, regExp) {
|
||||
let f = () => {};
|
||||
try {
|
||||
await fn();
|
||||
} catch(e) {
|
||||
f = () => { throw e; };
|
||||
} finally {
|
||||
assert.throws(f, regExp);
|
||||
}
|
||||
}
|
||||
|
||||
describe("Sum test", () => {
|
||||
it("Should compile a code with an undefined if", async () => {
|
||||
await compiler(path.join(__dirname, "circuits", "undefinedif.circom"));
|
||||
});
|
||||
it("Should compile a code with vars inside a for", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "forvariables.circom"));
|
||||
|
||||
const circuit = new snarkjs.Circuit(cirDef);
|
||||
|
||||
const witness = circuit.calculateWitness({ "in": 111});
|
||||
assert(witness[0].equals(bigInt(1)));
|
||||
assert(witness[1].equals(bigInt(114)));
|
||||
assert(witness[2].equals(bigInt(111)));
|
||||
|
||||
});
|
||||
it("Should compile a code with an undefined if", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "mixvarsignal.circom"));
|
||||
|
||||
const circuit = new snarkjs.Circuit(cirDef);
|
||||
|
||||
const witness = circuit.calculateWitness({ "i": 111});
|
||||
assert(witness[0].equals(bigInt(1)));
|
||||
assert(witness[1].equals(bigInt(111*111)));
|
||||
assert(witness[2].equals(bigInt(111)));
|
||||
});
|
||||
// it("Should assign signal ERROR", async () => {
|
||||
// await assertThrowsAsync(async () => {
|
||||
// await compiler(path.join(__dirname, "circuits", "assignsignal.circom"));
|
||||
// }, /Cannot assign to a signal .*/);
|
||||
// });
|
||||
it("Should compile a code with compute", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "compute.circom"));
|
||||
|
||||
const circuit = new snarkjs.Circuit(cirDef);
|
||||
|
||||
const witness = circuit.calculateWitness({ "x": 6});
|
||||
assert(witness[0].equals(bigInt(1)));
|
||||
assert(witness[1].equals(bigInt(37)));
|
||||
assert(witness[2].equals(bigInt(6)));
|
||||
});
|
||||
it("Should compile a code with compute", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "inout.circom"));
|
||||
|
||||
assert.equal(cirDef.constraints.length, 1);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
template Add() {
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
out <== in[0] + in[1];
|
||||
}
|
||||
|
||||
component main = Add();
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
|
||||
template AddConst(c) {
|
||||
signal input in;
|
||||
signal output out;
|
||||
var a = 2;
|
||||
var b = 3;
|
||||
a=a+b;
|
||||
a=a+4;
|
||||
a=a+c;
|
||||
|
||||
out <== 5 + a + in;
|
||||
}
|
||||
|
||||
// It should out <== in + 1+2+3+4+5 = in + 15
|
||||
component main = AddConst(1);
|
||||
@@ -1,40 +0,0 @@
|
||||
// arr1
|
||||
|
||||
|
||||
function Add3(arr1, arr2, arr3) {
|
||||
var res[3];
|
||||
|
||||
res[0] = arr1;
|
||||
res[1] = 0;
|
||||
for (var i=0; i<2; i += 1) {
|
||||
res[1] = res[1] + arr2[i];
|
||||
}
|
||||
|
||||
res[2] = 0;
|
||||
for (var i=0; i<2; i++) {
|
||||
for (var j=0; j<3; j += 1) {
|
||||
res[2] = res[2] + arr3[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template Main() {
|
||||
signal input in;
|
||||
signal output out[3];
|
||||
|
||||
var c[3] = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39];
|
||||
var d[3] = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]);
|
||||
|
||||
out[0] <-- d[0] + c[0];
|
||||
out[0] === in+c[0];
|
||||
|
||||
out[1] <-- d[1]+c[1];
|
||||
out[1] === 2*in+3+c[1];
|
||||
|
||||
out[2] <-- d[2]+c[2];
|
||||
out[2] === 6*in+12+c[2];
|
||||
}
|
||||
|
||||
component main = Main();
|
||||
@@ -1,7 +0,0 @@
|
||||
template A() {
|
||||
signal output out;
|
||||
|
||||
out = 3; // This is an error that compile should detect
|
||||
}
|
||||
|
||||
component main = A();
|
||||
@@ -1,28 +0,0 @@
|
||||
template Square() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
out <== in*in;
|
||||
}
|
||||
|
||||
template Main(n) {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
component squares[n];
|
||||
|
||||
var i;
|
||||
|
||||
for (i=0; i<n; i++) {
|
||||
squares[i] = Square();
|
||||
if (i==0) {
|
||||
squares[i].in <== in;
|
||||
} else {
|
||||
squares[i].in <== squares[i-1].out;
|
||||
}
|
||||
}
|
||||
|
||||
squares[n-1].out ==> out;
|
||||
}
|
||||
|
||||
component main = Main(3);
|
||||
@@ -1,27 +0,0 @@
|
||||
template Square() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
out <== in**2;
|
||||
}
|
||||
|
||||
template Main(n, nrounds) {
|
||||
signal input in[n];
|
||||
signal output out[n];
|
||||
|
||||
component squares[n][nrounds];
|
||||
|
||||
for (var i=0; i<n; i++) {
|
||||
for (var r=0; r<nrounds; r++) {
|
||||
squares[i][r] = Square();
|
||||
if (r==0) {
|
||||
squares[i][r].in <== in[i];
|
||||
} else {
|
||||
squares[i][r].in <== squares[i][r-1].out;
|
||||
}
|
||||
}
|
||||
squares[i][nrounds-1].out ==> out[i];
|
||||
}
|
||||
}
|
||||
|
||||
component main = Main(2, 3);
|
||||
@@ -1,17 +0,0 @@
|
||||
template X() {
|
||||
signal input x;
|
||||
signal output y;
|
||||
signal x2;
|
||||
signal x3;
|
||||
var a;
|
||||
compute {
|
||||
a = (x*x*x+6)/x;
|
||||
y <-- a;
|
||||
}
|
||||
|
||||
x2 <== x*x;
|
||||
x3 <== x2*x;
|
||||
x*y === x3+6;
|
||||
}
|
||||
|
||||
component main = X();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user