Browse Source

Fixes and tests passed

feature/witness_bin
Jordi Baylina 4 years ago
parent
commit
e62c1cdbc3
No known key found for this signature in database GPG Key ID: 7480C80C1BE43112
19 changed files with 925 additions and 283 deletions
  1. +24
    -2
      c/calcwit.cpp
  2. +2
    -0
      c/calcwit.h
  3. +6
    -14
      cli.js
  4. +376
    -182
      doc/r1cs_bin_format.md
  5. +1
    -0
      index.js
  6. +4
    -0
      src/c_build.js
  7. +21
    -8
      src/c_gen.js
  8. +70
    -10
      src/c_tester.js
  9. +7
    -58
      src/compiler.js
  10. +1
    -1
      src/ctx.js
  11. +9
    -3
      src/exec.js
  12. +3
    -2
      src/lcalgebra.js
  13. +289
    -0
      src/r1csfile.js
  14. +1
    -1
      src/utils.js
  15. +3
    -1
      src/zqfield.js
  16. +22
    -1
      test/basiccases.js
  17. +17
    -0
      test/circuits/constantcircuit.circom
  18. +18
    -0
      test/circuits/constantinternalcircuit.circom
  19. +51
    -0
      utils/mergesymbols.js

+ 24
- 2
c/calcwit.cpp

@ -122,14 +122,29 @@ void Circom_CalcWit::freeBigInts(PBigInt bi, int n) {
}
void Circom_CalcWit::getSignal(int cIdx, int sIdx, PBigInt value) {
#ifdef SANITY_CHECK
if (signalAssigned[sIdx] == false) {
fprintf(stderr, "Accessing a not assigned signal: %d\n", sIdx);
assert(false);
}
#endif
mpz_set(*value, signalValues[sIdx]);
}
void Circom_CalcWit::setSignal(int cIdx, int sIdx, PBigInt value) {
#ifdef SANITY_CHECK
assert(signalAssigned[sIdx] == false);
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);
printf("%d --> %s\n", sIdx, valueStr);
free(valueStr);
*/
mpz_set(signalValues[sIdx], *value);
if ( BITMAP_ISSET(circuit->mapIsInput, sIdx) ) {
inputSignalsToTrigger[cIdx]--;
@ -147,7 +162,7 @@ void Circom_CalcWit::checkConstraint(PBigInt value1, PBigInt value2, char const
std::string sV2 = std::string(pcV2);
free(pcV1);
free(pcV2);
throw std::runtime_error(std::string("Constraint does not match,") + err + ". " + sV1 + " != " + sV2 );
throw std::runtime_error(std::string("Constraint doesn't match, ") + err + ". " + sV1 + " != " + sV2 );
}
#endif
}
@ -160,3 +175,10 @@ void Circom_CalcWit::triggerComponent(int newCIdx) {
cIdx = oldCIdx;
}
void Circom_CalcWit::log(PBigInt value) {
char *pcV = mpz_get_str(0, 10, *value);
printf("Log: %s\n", pcV);
free(pcV);
}

+ 2
- 0
c/calcwit.h

@ -43,6 +43,8 @@ public:
void checkConstraint(PBigInt value1, PBigInt value2, char const *err);
void log(PBigInt value);
// Public functions
inline void setInput(int idx, PBigInt val) {

+ 6
- 14
cli.js

@ -67,7 +67,6 @@ if (argv._.length == 0) {
const fullFileName = path.resolve(process.cwd(), inputFile);
const fileName = path.basename(fullFileName, ".circom");
const outName = argv.output ? argv.output : fileName + ".json";
const cSourceName = typeof(argv.csource) === "string" ? argv.csource : fileName + ".cpp";
const r1csName = typeof(argv.r1cs) === "string" ? argv.r1cs : fileName + ".r1cs";
const symName = typeof(argv.sym) === "string" ? argv.sym : fileName + ".sym";
@ -79,24 +78,15 @@ if (argv.csource) {
options.cSourceWriteStream = fs.createWriteStream(cSourceName);
}
if (argv.r1cs) {
options.r1csWriteStream = fs.createWriteStream(r1csName);
options.r1csFileName = r1csName;
}
if (argv.sym) {
options.symWriteStream = fs.createWriteStream(symName);
}
compiler(fullFileName, options).then( () => {
let r1csDone = false;
let cSourceDone = false;
let symDone = false;
if (options.r1csWriteStream) {
options.r1csWriteStream.end(() => {
r1csDone = true;
finishIfDone();
});
} else {
r1csDone = true;
}
if (options.cSourceWriteStream) {
options.cSourceWriteStream.end(() => {
cSourceDone = true;
@ -111,11 +101,13 @@ compiler(fullFileName, options).then( () => {
finishIfDone();
});
} else {
cSourceDone = true;
symDone = true;
}
function finishIfDone() {
if ((r1csDone)&&(cSourceDone)&&(symDone)) {
process.exit(0);
if ((cSourceDone)&&(symDone)) {
setTimeout(() => {
process.exit(0);
}, 300);
}
}
}, (err) => {

+ 376
- 182
doc/r1cs_bin_format.md

@ -29,23 +29,26 @@ This standard specifies a format for a r1cs and allows the to connect a set of t
### General considerations
All integers are represented in Little Endian Fix size format
The standard extension is `.r1cs`
The constraint is in the form
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}s_0 + a_{0,1}s_1 + ... + a_{0,n-1}s_{n-1}) &\cdot& (b_{0,0} s_0 + b_{0,1} s_1 + ... + b_{0,n-1} s_{n-1}) &-& (c_{0,0} s_0 + c_{0,1} s_1 + ... + c_{0,n-1}s_{n-1}) &=& 0 \\
(a_{1,0}s_0 + a_{1,1}s_1 + ... + a_{1,n-1}s_{n-1}) &\cdot& (b_{1,0} s_0 + b_{1,1} s_1 + ... + b_{1,n-1} s_{n-1}) &-& (c_{1,0} s_0 + c_{1,1}s_1 + ... + c_{1,n-1}s_{n-1}) &=& 0 \\
(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}s_0 + a_{m-1,1}s_1 + ... + a_{m-1,n-1}s_{n-1}) &\cdot& (b_{m-1,0} s_0 + b_{m-1,1} s_1 + ... + b_{m-1,n-1} s_{n-1}) &-& (c_{m-1,0} s_0 + c_{m-1,1}s_1 + ... + c_{m-1,n-1}s_{n-1}) &=& 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
### Format of the file
````
@ -55,96 +58,285 @@ $$
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ 4 │ 01 00 00 00 ┃ Version 1
┗━━━━┻━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ 4 │ nW ┃
┃ 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
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ nW │ 01 00 00 00 ┃ nWires
4 │ 00 00 00 00 ┃ bigInt Format
┗━━━━┻━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ nW │ 01 00 00 00 ┃ nPubOut
4 │ is ┃ Id size ( Normally 4 (32bits))
┗━━━━┻━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ nW │ 01 00 00 00 ┃ nPubIn
is │ 01 00 00 00 ┃ nWires
┗━━━━┻━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ nW │ 01 00 00 00 ┃ nPrvIn
┃ is │ 01 00 00 00 ┃ nPubOut
┗━━━━┻━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ is │ 01 00 00 00 ┃ nPubIn
┗━━━━┻━━━━━━━━━━━━━━━━━┛
┏━━━━┳━━━━━━━━━━━━━━━━━┓
┃ nW │m := NConstraints┃
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
````
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
┃ nW │ nA ┃ ╲
is │ nA ┃ ╲
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
┃ nW │ idx_1 ┃ V │ a_{0,idx_1} ┃ │
is │ wireId_1 ┃ V │ a_{0,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
┃ nW │ idx_2 ┃ V │ a_{0,idx_2} ┃ │
is │ wireId_2 ┃ V │ a_{0,wireId_2} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
... ... │
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_nA ┃ V │ a_{0,idx_nA} ┃ │
is │ wireId_nA ┃ V │ a_{0,wireId_nA} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
┃ nW │ nB ┃ │
is │ nB ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_1 ┃ V │ b_{0,idx_1} ┃ │
is │ wireId_1 ┃ V │ b_{0,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
┃ nW │ idx_2 ┃ V │ b_{0,idx_2} ┃ ╲
is │ wireId_2 ┃ V │ b_{0,wireId_2} ┃ ╲
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_0
... ... ╱
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_nB ┃ V │ b_{0,idx_nB} ┃ │
is │ wireId_nB ┃ V │ b_{0,wireId_nB} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
┃ nW │ nC ┃ │
is │ nC ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_1 ┃ V │ c_{0,idx_1} ┃ │
is │ wireId_1 ┃ V │ c_{0,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
┃ nW │ idx_2 ┃ V │ c_{0,idx_2} ┃ │
is │ wireId_2 ┃ V │ c_{0,wireId_2} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
... ... │
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_nB ┃ V │ c_{0,idx_nC} ┃ ╱
is │ wireId_nC ┃ V │ c_{0,wireId_nC} ┃ ╱
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
┃ nW │ nA ┃ ╲
is │ nA ┃ ╲
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
┃ nW │ idx_1 ┃ V │ a_{1,idx_1} ┃ │
is │ wireId_1 ┃ V │ a_{1,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
┃ nW │ idx_2 ┃ V │ a_{1,idx_2} ┃ │
is │ wireId_2 ┃ V │ a_{1,wireId_2} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
... ... │
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_nA ┃ V │ a_{1,idx_nA} ┃ │
is │ wireId_nA ┃ V │ a_{1,wireId_nA} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
┃ nW │ nB ┃ │
is │ nB ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_1 ┃ V │ b_{1,idx_1} ┃ │
is │ wireId_1 ┃ V │ b_{1,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
┃ nW │ idx_2 ┃ V │ b_{1,idx_2} ┃ ╲
is │ wireId_2 ┃ V │ b_{1,wireId_2} ┃ ╲
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_1
... ... ╱
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_nB ┃ V │ b_{1,idx_nB} ┃ │
is │ wireId_nB ┃ V │ b_{1,wireId_nB} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
┃ nW │ nC ┃ │
is │ nC ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_1 ┃ V │ c_{1,idx_1} ┃ │
is │ wireId_1 ┃ V │ c_{1,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
┃ nW │ idx_2 ┃ V │ c_{1,idx_2} ┃ │
is │ wireId_2 ┃ V │ c_{1,wireId_2} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
... ... │
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
┃ nW │ idx_nB ┃ V │ c_{1,idx_nC} ┃ ╱
is │ wireId_nC ┃ V │ c_{1,wireId_nC} ┃ ╱
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
@ -153,114 +345,44 @@ $$
...
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
nW │ nA ┃ ╲
is │ nA ┃ ╲
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
nW │ idx_1 ┃ V │ a_{m-1,idx_1} ┃ │
is │ wireId_1 ┃ V │ a_{m-1,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
nW │ idx_2 ┃ V │ a_{m-1,idx_2} ┃ │
is │ wireId_2 ┃ V │ a_{m-1,wireId_2} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
... ... │
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
nW │ idx_nA ┃ V │ a_{m-1,idx_nA} ┃ │
is │ wireId_nA ┃ V │ a_{m-1,wireId_nA} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
nW │ nB ┃ │
is │ nB ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
nW │ idx_1 ┃ V │ b_{m-1,idx_1} ┃ │
is │ wireId_1 ┃ V │ b_{m-1,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
nW │ idx_2 ┃ V │ b_{m-1,idx_2} ┃ ╲
is │ wireId_2 ┃ V │ b_{m-1,wireId_2} ┃ ╲
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_{m-1}
... ... ╱
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
nW idx_nB ┃ V │ b_{m-1,idx_nB} ┃ │
┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
is │ wireId_nB ┃ V │ b_{m-1,wireId_nB} ┃ │
┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
nW │ nC ┃ │
is │ nC ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
nW │ idx_1 ┃ V │ c_{m-1,idx_1} ┃ │
is │ wireId_1 ┃ V │ c_{m-1,wireId_1} ┃ │
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
nW │ idx_2 ┃ V │ c_{m-1,idx_2} ┃ │
is │ wireId_2 ┃ V │ c_{m-1,wireId_2} ┃ │
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
... ... │
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
nW │ idx_nB ┃ V │ c_{m-1,idx_nC} ┃ ╱
is │ wireId_nC ┃ V │ c_{m-1,wireId_nC} ┃ ╱
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
╱ ╱
````
### Magic Number
Size: 4 bytes
The file start with a constant 4 byts (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
```
### Word With (nW)
Size: 4 bytes
Format: Little-Endian
This is the standard word size in bytes used to specify lenghts and indexes in the file.
The format of this field is little endian.
In most of the cases this will be 4 (32bit values)
Example:
```
0x04 0x00 0x00 0x00
```
### Number of wires
Size: nW bytes
Format: Little-Endian
Total Number of wires including ONE signal (Index 0).
### Number of public outputs
Size: nW bytes
Format: Little-Endian
Total Number of wires public output wires. They should be starting at idx 1
### Number of public inputs
Size: nW 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: nW bytes
Format: Little-Endian
Total Number of wires private input wires. They should be starting just after the public inputs
### Number of constraints
Size: nW bytes
Format: Little-Endian
Total Number of constraints
### Constraints
#### Constraints
Each constraint contains 3 linear combinations A, B, C.
@ -269,36 +391,35 @@ The constraint is such that:
A*B-C = 0
```
### Linear combination
#### Linear combination
Each linear combination is of the form:
$$
a_{0,0}s_0 + a_{0,1}s_1 + ... + a_{0,n-1}s_{n-1}
a_{j,0}w_0 + a_{j,1}w_1 + ... + a_{j,n}w_{n}
$$
### Number of nonZero Factors
#### Number of nonZero Factors
Size: nW bytes
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
#### Factor
For each factor we have the index of the factor and the value of the factor.
### Index of the factor
#### WireId of the factor
Size: nW bytes
Size: `is` bytes
Format: Little-Endian
Index of the nonZero Factor
WireId of the nonZero Factor
### Value of the factor
#### Value of the factor
The first byte indicate the length N in bytes of the number in the upcoming bytes.
@ -307,7 +428,7 @@ The next N bytes represent the value in Little Endian format.
For example, to represent the linear combination:
$$
5s_4 +8s_5 + 260s_886
5w_4 +8w_5 + 260w_{886}
$$
The linear combination would be represented as:
@ -323,6 +444,18 @@ The linear combination would be represented as:
┃ 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.
@ -331,6 +464,8 @@ 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
@ -344,37 +479,62 @@ Given this r1cs in a 256 bit Field:
$$
\left\{ \begin{array}{rclclcl}
(3s_5 + 8s_6) &\cdot& (2s_0 + 20s_2 + 12s_3) &-& (5s_0 + 7s_9) &=& 0 \\
(4s_1 + 8s_5 + 3s_9) &\cdot& (6s_6 + 44s_3) && &=& 0 \\
(4s_6) &\cdot& (6s_0 + 5s_3 + 11s_9) &-& (600s_700) &=& 0
(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
┏━━━━━━━━━━━━━━┓
┃ 72 31 63 77 ┃ Magic
┣━━━━━━━━━━━━━━┫
┃ 01 00 00 00 ┃ Version
┣━━━━━━━━━━━━━━┫
┃ 03 00 00 00 ┃ nSections
┗━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ 01 00 00 00 ┃ 49 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
┣━━━━━━━━━━━━━━┫
┃ 01 00 00 00 ┃ Version
┃ 04 00 00 00 ┃ Id Size
┣━━━━━━━━━━━━━━┫
┃ 04 00 00 00 ┃ nW
┣━━━━━━━━━━━━━━┫
┃ 04 23 45 00 ┃ # of wires
┃ 07 00 00 00 ┃ # of wires
┣━━━━━━━━━━━━━━┫
┃ 01 00 00 00 ┃ # Public Outs
┣━━━━━━━━━━━━━━┫
┃ 02 00 00 00 ┃ # Public Ins
┃ 02 00 00 00 ┃ # Public Ins
┣━━━━━━━━━━━━━━┫
┃ 05 00 00 00 ┃ # Private Ins
┗━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━┓
┃ 03 00 00 00 ┃ # of constraints
┃ 03 00 00 00 ┃ # Private Ins
┣━━━━━━━━━━━━━━┫
┃ e8 03 00 00 ┃ # Labels
┣━━━━━━━━━━━━━━┫
┃ 03 00 00 00 ┃ # Constraints
┗━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━┓ Constraint 0: (3s_5 + 8s_6) * (2s_0 + 20s_2 + 12s_3) - (5s_0 + 7s_9) = 0
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ 02 00 00 00 ┃ 8b 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 ┃
@ -395,18 +555,17 @@ The format will be:
┣━━━━━━━━━━━━━━╋━━━━━━━━┓
┃ 00 00 00 00 ┃ 01 05 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━┫
┃ 09 00 00 00 ┃ 01 07 ┃
┃ 02 00 00 00 ┃ 01 07 ┃
┗━━━━━━━━━━━━━━┻━━━━━━━━┛
┏━━━━━━━━━━━━━━┓ Constraint 1: (4s_1 + 8s_5 + 3s_9) * (6s_6 + 44s_3) = 0
┏━━━━━━━━━━━━━━┓ Constraint 1: (4w_1 + 8w_4 + 3w_5) * (6w_6 + 44w_3) = 0
┃ 03 00 00 00 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
┃ 01 00 00 00 ┃ 01 04 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
┃ 05 00 00 00 ┃ 01 08 ┃
┃ 04 00 00 00 ┃ 01 08 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
┃ 09 00 00 00 ┃ 01 03 ┃
┃ 05 00 00 00 ┃ 01 03 ┃
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛
┏━━━━━━━━━━━━━━┓
┃ 02 00 00 00 ┃
@ -419,8 +578,7 @@ The format will be:
┃ 00 00 00 00 ┃
┗━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━┓ Constraint 2: (4s_6) * (6s_0 + 5s_3 + 11s_9) - (600s_700) = 0
┏━━━━━━━━━━━━━━┓ Constraint 2: (4w_6) * (6w_0 + 5w_3 + 11w_2) - (600w_6) = 0
┃ 01 00 00 00 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
┃ 06 00 00 00 ┃ 01 04 ┃
@ -430,15 +588,34 @@ The format will be:
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓
┃ 00 00 00 00 ┃ 01 06 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
┃ 03 00 00 00 ┃ 01 05
┃ 02 00 00 00 ┃ 01 0B
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫
┃ 09 00 00 00 ┃ 01 0B
┃ 03 00 00 00 ┃ 01 05
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛
┏━━━━━━━━━━━━━━┓
┃ 01 00 00 00 ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━━━━━┓
BC 02 00 00 ┃ 02 58 02 ┃
06 00 00 00 ┃ 02 58 02 ┃
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ 03 00 00 00 ┃ 1c 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:
@ -446,38 +623,54 @@ 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
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
04 23 45 00
07 00 00 00
01 00 00 00
02 00 00 00
05 00 00 00
03 00 00 00
e8 03 00 00
03 00 00 00
02 00 00 00 8b 00 00 00
02 00 00 00
05 00 00 00 01 03
06 00 00 00 01 08
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
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
09 00 00 00 01 07
00 00 00 00 01 05
02 00 00 00 01 07
03 00 00 00
01 00 00 00 01 04
05 00 00 00 01 08
09 00 00 00 01 03
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
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
06 00 00 00 01 04
03 00 00 00
00 00 00 00 01 06
03 00 00 00 01 05
09 00 00 00 01 0B
00 00 00 00 01 06
02 00 00 00 01 0B
03 00 00 00 01 05
01 00 00 00
BC 02 00 00 02 58 02
06 00 00 00 02 58 02
03 00 00 00 1c 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
@ -487,3 +680,4 @@ circom will output this format.
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

+ 1
- 0
index.js

@ -1,2 +1,3 @@
module.exports.compiler = require("./src/compiler.js");
module.exports.c_tester = require("./src/c_tester.js");
module.exports.tester = require("./src/c_tester.js");

+ 4
- 0
src/c_build.js

@ -377,6 +377,8 @@ function buildFunction(name, paramValues) {
const oldUniqueNames = ctx.uniqueNames;
const oldFileName = ctx.fileName;
const oldFilePath = ctx.oldFilePath;
const oldReturnSizes = ctx.returnSizes;
const oldReturnValue = ctx.returnValue;
ctx.scopes = [{}];
@ -453,6 +455,8 @@ function buildFunction(name, paramValues) {
ctx.uniqueNames = oldUniqueNames;
ctx.fileName = oldFileName;
ctx.filePath = oldFilePath;
ctx.returnSizes = oldReturnSizes;
ctx.returnValue = oldReturnValue;
ctx.definedFunctions[h] = res;

+ 21
- 8
src/c_gen.js

@ -470,9 +470,9 @@ function genVariable(ctx, ast) {
const resRef = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
const res = ctx.refs[resRef];
res.used = true;
ctx.codeHeader += `PBigInt ${res};\n`;
ctx.codeHeader += `PBigInt ${res.label};\n`;
ctx.code += `${res.label} = ${v.label} + ${offset.label};\n`;
return res;
return resRef;
} else {
// return newSubRef(ctx, ast.name, ast.selectors);
return newRef(ctx, "BIGINT", "_v", v.value.slice(offset.value[0], offset.value[0] + v.sizes[l]),v.sizes.slice(l));
@ -574,6 +574,9 @@ function genGetSignalSizes(ctx, cIdxRef, label) {
function genSetSignal(ctx, cIdxRef, sIdxRef, valueRef) {
const v = ctx.refs[valueRef];
if (!utils.isDefined(v)) {
console.log("BREAK!!!");
}
if (!v.used) {
instantiateRef(ctx, valueRef, v.value);
}
@ -742,8 +745,8 @@ function genConstraint(ctx, ast) {
const b = ctx.refs[bRef];
if (ctx.error) return;
const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
instantiateRef(ctx, aRef);
instantiateRef(ctx, bRef);
instantiateRef(ctx, aRef, a.value);
instantiateRef(ctx, bRef, b.value);
ctx.code += `ctx->checkConstraint(${a.label}, ${b.label}, "${strErr}");`;
}
@ -792,6 +795,13 @@ function genArray(ctx, ast) {
function genFunctionCall(ctx, ast) {
if (ast.name == "log") {
const vRef = gen(ctx, ast.params[0]);
const val = ctx.refs[vRef];
instantiateRef(ctx, vRef, val.value);
ctx.code+=`ctx->log(${val.label});`;
return vRef;
}
const params = [];
for (let i=0; i<ast.params.length; i++) {
const pRef = gen(ctx, ast.params[i]);
@ -913,9 +923,7 @@ function genLoop(ctx, ast) {
condVar = ctx.refs[condVarRef];
instantiateRef(ctx, condVarRef);
ctx.code =
oldCode +
ctx.code +
ctx.code +=
`${condVar.label} = ctx->field->isTrue(${cond2.label});\n` +
`while (${condVar.label}) {\n`;
} else {
@ -972,6 +980,7 @@ function genIf(ctx, ast) {
}
ctx.code += "}\n";
leaveConditionalCode(ctx);
} else {
if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
@ -990,7 +999,7 @@ function genReturn(ctx, ast) {
const vRef = gen(ctx, ast.value);
const v= ctx.refs[vRef];
if (ctx.returnSizes) {
if (!utils.sizesEqual(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes");
if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes");
} else {
ctx.returnSizes = v.sizes;
}
@ -1127,6 +1136,8 @@ function genTerCon(ctx, ast) {
if (ctx.error) return;
const then = ctx.refs[thenRef];
instantiateRef(ctx, thenRef, then.value);
ctx.code = oldCode + utils.ident(ctx.code);
ctx.code += `${rLabel} = ${then.label};\n`;
@ -1139,6 +1150,8 @@ function genTerCon(ctx, ast) {
if (ctx.error) return;
const els = ctx.refs[elseRef];
instantiateRef(ctx, elseRef, els.value);
ctx.code = oldCode + utils.ident(ctx.code);
ctx.code += `${rLabel} = ${els.label};\n`;

+ 70
- 10
src/c_tester.js

@ -10,14 +10,16 @@ const exec = util.promisify(require("child_process").exec);
const stringifyBigInts = require("./utils").stringifyBigInts;
const unstringifyBigInts = require("./utils").unstringifyBigInts;
const bigInt = require("snarkjs").bigInt;
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, mainComponent, _options) {
async function c_tester(circomFile, _options) {
tmp.setGracefulCleanup();
mainComponent = mainComponent || "main";
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
@ -26,31 +28,34 @@ async function c_tester(circomFile, mainComponent, _options) {
options.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp"));
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
options.mainComponent = mainComponent;
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(dir.path, baseName + ".cpp")} ` +
` ${path.join(cdir, "main.cpp")}` +
` ${path.join(cdir, "calcwit.cpp")}` +
` ${path.join(cdir, "utils.cpp")}` +
` ${path.join(cdir, "zqfield.cpp")}` +
` ${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, mainComponent);
return new CTester(dir, baseName);
}
class CTester {
constructor(dir, baseName, mainComponent) {
constructor(dir, baseName) {
this.dir=dir;
this.baseName = baseName;
this.mainComponent = mainComponent;
}
async release() {
@ -74,7 +79,8 @@ class CTester {
return res;
}
async _loadSymbols() {
async loadSymbols() {
if (this.symbols) return;
this.symbols = {};
const symsStr = await fs.promises.readFile(
path.join(this.dir.path, this.baseName + ".sym"),
@ -91,9 +97,18 @@ class CTester {
}
}
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();
if (!self.symbols) await self.loadSymbols();
checkObject("main", expectedOut);
@ -118,6 +133,51 @@ class CTester {
}
}
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;
}
}
}

+ 7
- 58
src/compiler.js

@ -21,7 +21,6 @@ const fs = require("fs");
const path = require("path");
const bigInt = require("big-integer");
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const __MASK__ = new bigInt(2).pow(253).minus(1);
const sONE = 0;
const assert = require("assert");
const buildC = require("./c_build");
@ -30,6 +29,7 @@ const lc = require("./lcalgebra");
const Ctx = require("./ctx");
const ZqField = require("./zqfield");
const utils = require("./utils");
const buildR1cs = require("./r1csfile").buildR1cs;
module.exports = compile;
@ -85,6 +85,7 @@ async function compile(srcFile, options) {
// 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);
}
@ -105,8 +106,8 @@ async function compile(srcFile, options) {
// const mainCode = gen(ctx,ast);
if (ctx.error) throw(ctx.error);
if (options.r1csWriteStream) {
buildR1cs(ctx, options.r1csWriteStream);
if (options.r1csFileName) {
await buildR1cs(ctx, options.r1csFileName);
}
if (options.symWriteStream) {
@ -505,63 +506,9 @@ function buildConstraints(ctx) {
return res;
}
function buildR1cs(ctx, strm) {
strm.write(Buffer.from([0x72,0x31,0x63,0x73]));
writeU32(1);
writeU32(4);
writeU32(1 + ctx.totals.output + ctx.totals.pubInput + ctx.totals.prvInput + ctx.totals.internal);
writeU32(ctx.totals.output);
writeU32(ctx.totals.pubInput);
writeU32(ctx.totals.prvInput);
writeU32(ctx.constraints.length);
for (let i=0; i<ctx.constraints.length; i++) {
if ((ctx.verbose)&&(i%10000 == 0)) console.log("writing constraint: ", i);
writeConstraint(ctx.constraints[i]);
}
function writeU32(v) {
const b = Buffer.allocUnsafe(4);
b.writeInt32LE(v);
strm.write(b);
}
function writeConstraint(c) {
writeLC(c.a);
writeLC(c.b);
writeLC(lc.negate(c.c));
}
function writeLC(lc) {
const idxs = Object.keys(lc.values);
writeU32(idxs.length);
for (let s in lc.values) {
let lSignal = ctx.signals[s];
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
writeU32(lSignal.id);
writeBigInt(lc.values[s]);
}
}
function writeBigInt(n) {
const bytes = [];
let r = bigInt(n);
while (r.greater(bigInt.zero)) {
bytes.push(r.and(bigInt("255")).toJSNumber());
r = r.shiftRight(8);
}
assert(bytes.length<=32);
assert(bytes.length>0);
strm.write( Buffer.from([bytes.length, ...bytes ]));
}
}
function buildSyms(ctx, strm) {
let nSyms;
addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
@ -581,6 +528,8 @@ function buildSyms(ctx, strm) {
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);
}

+ 1
- 1
src/ctx.js

@ -188,7 +188,7 @@ module.exports = class Ctx {
errStr: errStr,
ast: ast,
message: errStr,
errFile: ast.fileName
errFile: this.fileName
};
} else {
return {

+ 9
- 3
src/exec.js

@ -261,7 +261,13 @@ function getScopeRef(ctx, name, selectors) {
}
for (let i=ctx.scopes.length-1; i>=0; i--) {
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name].value, sels, ctx.scopes[i][name].type);
if (ctx.scopes[i][name]) {
if (ctx.scopes[i][name].type == "COMPONENT") {
return [null, sels, "COMPONENT"];
} else {
return select(ctx.scopes[i][name].value, sels, ctx.scopes[i][name].type);
}
}
}
return [null, [], ""];
}
@ -766,6 +772,7 @@ function execVarAssignement(ctx, ast) {
} else {
v = ast.values[0];
}
const [num, sels, typ] = getScopeRef(ctx, v.name, v.selectors);
if (ctx.error) return;
@ -1265,10 +1272,9 @@ function execConstrain(ctx, ast) {
if (!lc.isZero(res)) {
ctx.constraints.push(lc.toQEQ(res));
if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
}
if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
return res;
}

+ 3
- 2
src/lcalgebra.js

@ -61,6 +61,7 @@ QEQ QEQ ERR ERR
const bigInt = require("big-integer");
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const sONE = 0;
const utils = require("./utils.js");
exports.add = add;
exports.mul = mul;
@ -439,8 +440,8 @@ function canonize(ctx, a) {
for (let k in a.values) {
let s = k;
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
if ((typeof(ctx.signals[s].value) != "undefined")&&(k != sONE)) {
const v = res.values[k].times(ctx.signals[s].value).mod(__P__);
if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
const v = res.values[k].times(ctx.signals[s].v).mod(__P__);
if (!res.values[sONE]) {
res.values[sONE]=v;
} else {

+ 289
- 0
src/r1csfile.js

@ -0,0 +1,289 @@
const fs = require("fs");
const assert = require("assert");
const lc = require("./lcalgebra");
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 readU32();
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 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 writeU32(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 - 4;
// Write constraints
///////////
await writeU32(2); // Constraints type
const pConstraintsSize = p;
await writeU32(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 - 4;
// Write map
///////////
await writeU32(3); // wires2label type
const pMapSize = p;
await writeU32(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 -4;
// Write sizes
await writeU32(headerSize, pHeaderSize);
await writeU32(fieldDefSize, pFieldDefSize);
await writeU32(constraintsSize, pConstraintsSize);
await writeU32(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 writeConstraint(c) {
await writeLC(c.a);
await writeLC(c.b);
await writeLC(lc.negate(c.c));
}
async function writeLC(lc) {
const idxs = Object.keys(lc.values);
await writeU32(idxs.length);
for (let s in lc.values) {
let lSignal = ctx.signals[s];
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
await writeU32(lSignal.id);
await writeBigInt(lc.values[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
- 1
src/utils.js

@ -99,7 +99,7 @@ function unstringifyBigInts(o) {
}
return res;
} else {
return o;
return bigInt(o);
}
}

+ 3
- 1
src/zqfield.js

@ -3,6 +3,8 @@ const assert = require("assert");
module.exports = class ZqField {
constructor(p) {
this.one = bigInt.one;
this.zero = bigInt.zero;
this.p = p;
this.bitLength = p.bitLength();
this.mask = bigInt.one.shiftLeft(this.bitLength - 1).minus(bigInt.one);
@ -11,7 +13,7 @@ module.exports = class ZqField {
add(a, b) {
let res = a.add(b);
if (res.geq(this.p)) {
res = res.minsu(this.p);
res = res.minus(this.p);
}
return res;
}

+ 22
- 1
test/basiccases.js

@ -44,7 +44,7 @@ async function doTest(circuit, testVectors) {
describe("basic cases", function () {
this.timeout(100000);
it("inout", async () => {
/* it("inout", async () => {
await doTest(
"inout.circom",
[
@ -298,4 +298,25 @@ describe("basic cases", function () {
]
);
});
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",
[
// 0xbb67ae85
[{in: 1}, {out: 5}],
[{in: 0}, {out: 4}],
[{in: -2}, {out: 2}],
[{in: 10}, {out: 14}]
]
);
});
});

+ 17
- 0
test/circuits/constantcircuit.circom

@ -0,0 +1,17 @@
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;
}
}
component main = H(1);

+ 18
- 0
test/circuits/constantinternalcircuit.circom

@ -0,0 +1,18 @@
template Const() {
signal output out[2];
out[0] <== 2;
out[1] <== 2;
}
template Main() {
signal input in;
signal output out;
component const = Const();
out <== const.out[0] + const.out[1] + in;
}
component main = Main();

+ 51
- 0
utils/mergesymbols.js

@ -0,0 +1,51 @@
const fs = require("fs");
const argv = require("yargs")
.usage("mergesymbols -i [input_file] -o [output_file] -s [symbols file]")
.alias("i", "input")
.alias("o", "output")
.alias("s", "symbols")
.help("h")
.epilogue(`Copyright (C) 2018 0kims association
This program comes with ABSOLUTELY NO WARRANTY;
This is free software, and you are welcome to redistribute it
under certain conditions; see the COPYING file in the official
repo directory at https://github.com/iden3/circom `)
.demandOption(["i","o","s"])
.argv;
const inFileName = argv.input;
const outFile = argv.output;
const symbolsFile = argv.symbols;
let symbols;
async function loadSymbols() {
symbols = {};
const symsStr = await fs.promises.readFile(symbolsFile,"utf8");
const lines = symsStr.split("\n");
for (let i=0; i<lines.length; i++) {
const arr = lines[i].split(",");
if (arr.length!=3) continue;
symbols[arr[0]] = arr[2];
}
}
async function run() {
const outLines = [];
await loadSymbols();
const inStr = await fs.promises.readFile(inFileName,"utf8");
const lines = inStr.split("\n");
for (let i=0; i<lines.length; i++) {
const arr = lines[i].split(" --> ");
if (arr.length!=2) continue;
outLines.push(symbols[arr[0]] + " --> " + arr[1]);
}
await fs.promises.writeFile(outFile,outLines.join("\n"), "utf8");
}
run().then(() => {
process.exit(0);
});

Loading…
Cancel
Save