@ -0,0 +1,155 @@ |
|||
#include <string>
|
|||
#include <stdexcept>
|
|||
#include <sstream>
|
|||
#include <iostream>
|
|||
#include <iomanip>
|
|||
#include <stdlib.h>
|
|||
#include <gmp.h>
|
|||
#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
|
|||
|
|||
inputSignalsToTrigger = new int[circuit->NComponents]; |
|||
signalValues = new BigInt[circuit->NSignals]; |
|||
|
|||
// Set one signal
|
|||
mpz_init_set_ui(signalValues[0], 1); |
|||
|
|||
// Initialize remaining signals
|
|||
for (int i=1; i<circuit->NSignals; i++) mpz_init2(signalValues[i], 256); |
|||
|
|||
reset(); |
|||
|
|||
} |
|||
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
|
|||
Circom_CalcWit::~Circom_CalcWit() { |
|||
#ifdef SANITY_CHECK
|
|||
delete signalAssigned; |
|||
#endif
|
|||
|
|||
for (int i=0; i<circuit->NSignals; i++) mpz_clear(signalValues[i]); |
|||
|
|||
delete[] signalValues; |
|||
delete inputSignalsToTrigger; |
|||
|
|||
} |
|||
|
|||
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; |
|||
} |
|||
|
|||
PBigInt Circom_CalcWit::allocBigInts(Circom_Sizes sizes) { |
|||
PBigInt res = new BigInt[sizes[0]]; |
|||
for (int i=0; i<sizes[0]; i++) mpz_init2(res[i], 256); |
|||
return res; |
|||
} |
|||
|
|||
void Circom_CalcWit::freeBigInts(PBigInt bi, Circom_Sizes sizes) { |
|||
for (int i=0; i<sizes[0]; i++) mpz_clear(bi[i]); |
|||
delete[] bi; |
|||
} |
|||
|
|||
void Circom_CalcWit::getSignal(int cIdx, int sIdx, PBigInt value) { |
|||
mpz_set(*value, signalValues[sIdx]); |
|||
} |
|||
|
|||
void Circom_CalcWit::setSignal(int cIdx, int sIdx, PBigInt value) { |
|||
#ifdef SANITY_CHECK
|
|||
assert(signalAssigned[sIdx] == false); |
|||
signalAssigned[sIdx] = true; |
|||
#endif
|
|||
mpz_set(signalValues[sIdx], *value); |
|||
if ( BITMAP_ISSET(circuit->mapIsInput, sIdx) ) { |
|||
inputSignalsToTrigger[cIdx]--; |
|||
if (inputSignalsToTrigger[cIdx] == 0) triggerComponent(cIdx); |
|||
} |
|||
|
|||
} |
|||
|
|||
void Circom_CalcWit::checkConstraint(PBigInt value1, PBigInt value2, char const *err) { |
|||
#ifdef SANITY_CHECK
|
|||
if (mpz_cmp(*value1, *value2) != 0) { |
|||
char *pcV1 = mpz_get_str(0, 10, *value1); |
|||
char *pcV2 = mpz_get_str(0, 10, *value1); |
|||
std::string sV1 = std::string(pcV1); |
|||
std::string sV2 = std::string(pcV2); |
|||
free(pcV1); |
|||
free(pcV2); |
|||
throw std::runtime_error(std::string("Constraint does not match,") + err + ". " + sV1 + " != " + sV2 ); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
|
|||
void Circom_CalcWit::triggerComponent(int newCIdx) { |
|||
int oldCIdx = cIdx; |
|||
cIdx = newCIdx; |
|||
(*(circuit->components[newCIdx].fn))(this); |
|||
cIdx = oldCIdx; |
|||
} |
|||
|
@ -0,0 +1,61 @@ |
|||
#ifndef CIRCOM_CALCWIT_H |
|||
#define CIRCOM_CALCWIT_H |
|||
|
|||
#include "circom.h" |
|||
|
|||
class Circom_CalcWit { |
|||
|
|||
#ifdef SANITY_CHECK |
|||
bool *signalAssigned; |
|||
#endif |
|||
|
|||
// componentStatus -> For each component |
|||
// >0 Signals required to trigger |
|||
// == 0 Component triggered |
|||
int *inputSignalsToTrigger; |
|||
|
|||
BigInt *signalValues; |
|||
|
|||
Circom_Circuit *circuit; |
|||
|
|||
|
|||
|
|||
void triggerComponent(int newCIdx); |
|||
void calculateWitness(void *input, void *output); |
|||
|
|||
|
|||
public: |
|||
int cIdx; |
|||
// 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); |
|||
|
|||
PBigInt allocBigInts(Circom_Sizes sizes); |
|||
void freeBigInts(PBigInt bi, Circom_Sizes sizes); |
|||
|
|||
void getSignal(int cIdx, int sIdx, PBigInt value); |
|||
void setSignal(int cIdx, int sIdx, PBigInt value); |
|||
|
|||
void checkConstraint(PBigInt value1, PBigInt value2, char const *err); |
|||
|
|||
|
|||
// Public functions |
|||
inline void setInput(int idx, PBigInt val) { |
|||
setSignal(0, circuit->wit2sig[idx], val); |
|||
} |
|||
inline void getWitness(int idx, PBigInt val) { |
|||
mpz_set(*val, signalValues[circuit->wit2sig[idx]]); |
|||
} |
|||
|
|||
void reset(); |
|||
|
|||
}; |
|||
|
|||
|
|||
|
|||
#endif // CIRCOM_CALCWIT_H |
@ -0,0 +1,56 @@ |
|||
#ifndef __CIRCOM_H |
|||
#define __CIRCOM_H |
|||
|
|||
#include <gmp.h> |
|||
#include <stdint.h> |
|||
|
|||
class Circom_CalcWit; |
|||
typedef unsigned long long u64; |
|||
typedef uint32_t u32; |
|||
typedef uint8_t u8; |
|||
typedef mpz_t BigInt; |
|||
typedef BigInt *PBigInt; |
|||
|
|||
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); |
|||
|
|||
struct Circom_Component { |
|||
Circom_HashTable hashTable; |
|||
Circom_ComponentEntries entries; |
|||
Circom_ComponentFunction fn; |
|||
int inputSignals; |
|||
}; |
|||
|
|||
class Circom_Circuit { |
|||
public: |
|||
int NSignals; |
|||
int NComponents; |
|||
int NInputs; |
|||
int NOutputs; |
|||
int NVars; |
|||
int *wit2sig; |
|||
Circom_Component *components; |
|||
u32 *mapIsInput; |
|||
}; |
|||
|
|||
#define BITMAP_ISSET(m, b) (m[b>>5] & (1 << (b&0x1F))) |
|||
extern struct Circom_Circuit _circuit; |
|||
|
|||
#endif |
@ -0,0 +1,196 @@ |
|||
#include <iostream>
|
|||
#include <fstream>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <sys/types.h>
|
|||
#include <sys/stat.h>
|
|||
#include <sys/mman.h>
|
|||
#include <fcntl.h>
|
|||
#include <gmp.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); |
|||
|
|||
BigInt v; |
|||
mpz_init2(v, 256); |
|||
u8 *p = in; |
|||
for (int i=0; i<_circuit.NInputs; i++) { |
|||
int len = *(u8 *)p; |
|||
p++; |
|||
mpz_import(v,len , -1 , 1, 0, 0, p); |
|||
p+=len; |
|||
ctx->setSignal(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) { |
|||
|
|||
BigInt v; |
|||
mpz_init2(v, 256); |
|||
|
|||
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"); |
|||
} |
|||
|
|||
mpz_set_str (v, s.c_str(), 10); |
|||
|
|||
ctx->setSignal(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 = ctx->getSignalOffset(0, h); |
|||
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"); |
|||
|
|||
BigInt v; |
|||
mpz_init2(v, 256); |
|||
|
|||
u8 buffOut[256]; |
|||
for (int i=0;i<_circuit.NVars;i++) { |
|||
size_t size=256; |
|||
ctx->getWitness(i, &v); |
|||
mpz_export(buffOut+1, &size, -1, 1, -1, 0, v); |
|||
*buffOut = (u8)size; |
|||
fwrite(buffOut, size+1, 1, write_ptr); |
|||
} |
|||
fclose(write_ptr); |
|||
|
|||
} |
|||
|
|||
|
|||
void writeOutJson(Circom_CalcWit *ctx, std::string filename) { |
|||
|
|||
std::ofstream outFile; |
|||
outFile.open (filename); |
|||
|
|||
outFile << "[\n"; |
|||
|
|||
BigInt v; |
|||
mpz_init2(v, 256); |
|||
|
|||
char pcV[256]; |
|||
|
|||
for (int i=0;i<_circuit.NVars;i++) { |
|||
ctx->getWitness(i, &v); |
|||
mpz_get_str(pcV, 10, v); |
|||
std::string sV = std::string(pcV); |
|||
outFile << (i ? "," : " ") << "\"" << sV << "\"\n"; |
|||
} |
|||
|
|||
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[]) { |
|||
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)"); |
|||
} |
|||
|
|||
|
|||
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); |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
#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); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,25 @@ |
|||
#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; |
|||
} |
@ -0,0 +1,10 @@ |
|||
#ifndef __UTILS_H |
|||
#define __UTILS_H |
|||
|
|||
#include "circom.h" |
|||
|
|||
std::string int_to_hex( u64 i ); |
|||
u64 fnv1a(std::string s); |
|||
|
|||
|
|||
#endif // __UTILS_H |
@ -0,0 +1,489 @@ |
|||
# 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 |
|||
|
|||
All integers are represented in Little Endian Fix size format |
|||
|
|||
The standard extension is `.r1cs` |
|||
|
|||
The constraint is in 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_{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 |
|||
\end{array} \right. |
|||
$$ |
|||
|
|||
|
|||
### Format |
|||
|
|||
```` |
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ 4 │ 72 31 63 73 ┃ Magic "r1cs" |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ 4 │ 01 00 00 00 ┃ Version 1 |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ 4 │ nW ┃ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ nW │ 01 00 00 00 ┃ nWires |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ nW │ 01 00 00 00 ┃ nPubOut |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ nW │ 01 00 00 00 ┃ nPubIn |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ nW │ 01 00 00 00 ┃ nPrvIn |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
|||
┃ nW │m := NConstraints┃ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲ |
|||
┃ nW │ nA ┃ ╲ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲ |
|||
┃ nW │ idx_1 ┃ V │ a_{0,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
|||
┃ nW │ idx_2 ┃ V │ a_{0,idx_2} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
... ... │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nA ┃ V │ a_{0,idx_nA} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ nB ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_1 ┃ V │ b_{0,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲ |
|||
┃ nW │ idx_2 ┃ V │ b_{0,idx_2} ┃ ╲ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_0 |
|||
... ... ╱ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nB ┃ V │ b_{0,idx_nB} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ nC ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_1 ┃ V │ c_{0,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
|||
┃ nW │ idx_2 ┃ V │ c_{0,idx_2} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
... ... │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nB ┃ V │ c_{0,idx_nC} ┃ ╱ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ |
|||
╱ |
|||
|
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲ |
|||
┃ nW │ nA ┃ ╲ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲ |
|||
┃ nW │ idx_1 ┃ V │ a_{1,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
|||
┃ nW │ idx_2 ┃ V │ a_{1,idx_2} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
... ... │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nA ┃ V │ a_{1,idx_nA} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ nB ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_1 ┃ V │ b_{1,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲ |
|||
┃ nW │ idx_2 ┃ V │ b_{1,idx_2} ┃ ╲ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_1 |
|||
... ... ╱ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nB ┃ V │ b_{1,idx_nB} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ nC ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_1 ┃ V │ c_{1,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
|||
┃ nW │ idx_2 ┃ V │ c_{1,idx_2} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
... ... │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nB ┃ V │ c_{1,idx_nC} ┃ ╱ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ |
|||
╱ |
|||
|
|||
... |
|||
... |
|||
... |
|||
|
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲ |
|||
┃ nW │ nA ┃ ╲ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲ |
|||
┃ nW │ idx_1 ┃ V │ a_{m-1,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
|||
┃ nW │ idx_2 ┃ V │ a_{m-1,idx_2} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
... ... │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nA ┃ V │ a_{m-1,idx_nA} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ nB ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_1 ┃ V │ b_{m-1,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲ |
|||
┃ nW │ idx_2 ┃ V │ b_{m-1,idx_2} ┃ ╲ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_{m-1} |
|||
... ... ╱ |
|||
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW idx_nB ┃ V │ b_{m-1,idx_nB} ┃ │ |
|||
┗━━━━━━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ nC ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_1 ┃ V │ c_{m-1,idx_1} ┃ │ |
|||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
|||
┃ nW │ idx_2 ┃ V │ c_{m-1,idx_2} ┃ │ |
|||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
|||
... ... │ |
|||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
|||
┃ nW │ idx_nB ┃ V │ c_{m-1,idx_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 |
|||
|
|||
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_{0,0}s_0 + a_{0,1}s_1 + ... + a_{0,n-1}s_{n-1} |
|||
$$ |
|||
|
|||
### Number of nonZero Factors |
|||
|
|||
Size: nW 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. |
|||
|
|||
### Index of the factor |
|||
|
|||
|
|||
Size: nW bytes |
|||
Format: Little-Endian |
|||
|
|||
Index 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: |
|||
|
|||
$$ |
|||
5s_4 +8s_5 + 260s_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 ┃ |
|||
┗━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┛ |
|||
```` |
|||
## 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. |
|||
|
|||
|
|||
## Backward Compatibility |
|||
|
|||
N.A. |
|||
|
|||
## Test Cases |
|||
### Example |
|||
|
|||
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 |
|||
\end{array} \right. |
|||
$$ |
|||
|
|||
The format will be: |
|||
|
|||
```` |
|||
|
|||
┏━━━━━━━━━━━━━━┓ |
|||
┃ 72 31 63 77 ┃ Magic |
|||
┣━━━━━━━━━━━━━━┫ |
|||
┃ 01 00 00 00 ┃ Version |
|||
┣━━━━━━━━━━━━━━┫ |
|||
┃ 04 00 00 00 ┃ nW |
|||
┣━━━━━━━━━━━━━━┫ |
|||
┃ 04 23 45 00 ┃ # of wires |
|||
┣━━━━━━━━━━━━━━┫ |
|||
┃ 01 00 00 00 ┃ # Public Outs |
|||
┣━━━━━━━━━━━━━━┫ |
|||
┃ 02 00 00 00 ┃ # Public Ins |
|||
┣━━━━━━━━━━━━━━┫ |
|||
┃ 05 00 00 00 ┃ # Private Ins |
|||
┗━━━━━━━━━━━━━━┛ |
|||
|
|||
┏━━━━━━━━━━━━━━┓ |
|||
┃ 03 00 00 00 ┃ # of constraints |
|||
┗━━━━━━━━━━━━━━┛ |
|||
|
|||
┏━━━━━━━━━━━━━━┓ Constraint 0: (3s_5 + 8s_6) * (2s_0 + 20s_2 + 12s_3) - (5s_0 + 7s_9) = 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 ┃ |
|||
┣━━━━━━━━━━━━━━╋━━━━━━━━┫ |
|||
┃ 09 00 00 00 ┃ 01 07 ┃ |
|||
┗━━━━━━━━━━━━━━┻━━━━━━━━┛ |
|||
|
|||
|
|||
┏━━━━━━━━━━━━━━┓ Constraint 1: (4s_1 + 8s_5 + 3s_9) * (6s_6 + 44s_3) = 0 |
|||
┃ 03 00 00 00 ┃ |
|||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓ |
|||
┃ 01 00 00 00 ┃ 01 04 ┃ |
|||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
|||
┃ 05 00 00 00 ┃ 01 08 ┃ |
|||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
|||
┃ 09 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: (4s_6) * (6s_0 + 5s_3 + 11s_9) - (600s_700) = 0 |
|||
┃ 01 00 00 00 ┃ |
|||
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓ |
|||
┃ 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 ┃ |
|||
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛ |
|||
┏━━━━━━━━━━━━━━┓ |
|||
┃ 01 00 00 00 ┃ |
|||
┣━━━━━━━━━━━━━━╋━━━━━━━━━━━━━┓ |
|||
┃ BC 02 00 00 ┃ 02 58 02 ┃ |
|||
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┛ |
|||
```` |
|||
|
|||
And the binary representation in Hex: |
|||
|
|||
```` |
|||
72 31 63 77 |
|||
01 00 00 00 |
|||
04 00 00 00 |
|||
04 23 45 00 |
|||
01 00 00 00 |
|||
02 00 00 00 |
|||
05 00 00 00 |
|||
03 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 |
|||
09 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 |
|||
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 |
|||
03 00 00 00 01 05 |
|||
09 00 00 00 01 0B |
|||
01 00 00 00 |
|||
BC 02 00 00 02 58 02 |
|||
```` |
|||
|
|||
## Implementation |
|||
|
|||
circom will output this format. |
|||
|
|||
## Copyright |
|||
|
|||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
@ -1 +1,2 @@ |
|||
module.exports = require("./src/compiler.js"); |
|||
module.exports.compiler = require("./src/compiler.js"); |
|||
module.exports.c_tester = require("./src/c_tester.js"); |
@ -0,0 +1,52 @@ |
|||
|
|||
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) { |
|||
|
|||
|
|||
} |
@ -0,0 +1,377 @@ |
|||
/* |
|||
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 utils = require("./utils"); |
|||
const assert = require("assert"); |
|||
const gen = require("./c_gen"); |
|||
|
|||
module.exports = buildC; |
|||
|
|||
|
|||
function buildC(ctx) { |
|||
ctx.code = ""; |
|||
ctx.tmpNames = {}; |
|||
ctx.getTmpName = getTmpName; |
|||
ctx.codes_sizes = []; |
|||
ctx.definedSizes = {}; |
|||
ctx.addSizes = addSizes; |
|||
|
|||
const entryTables = buildEntryTables(ctx); |
|||
const code = buildCode(ctx); |
|||
const compnentsArray = buildComponentsArray(ctx); |
|||
|
|||
const headder = buildHeader(ctx); |
|||
const sizes = buildSizes(ctx); |
|||
const mapIsInput = buildMapIsInput(ctx); |
|||
const wit2Sig = buildWit2Sig(ctx); |
|||
const circuitVar = buildCircuitVar(ctx); |
|||
|
|||
return "" + |
|||
headder + "\n" + |
|||
sizes + "\n" + |
|||
entryTables + "\n" + |
|||
code + "\n" + |
|||
compnentsArray + "\n" + |
|||
mapIsInput + "\n" + |
|||
wit2Sig +"\n" + |
|||
circuitVar; |
|||
} |
|||
|
|||
function buildEntryTables(ctx) { |
|||
|
|||
const codes_hashMaps = []; |
|||
const codes_componentEntries = []; |
|||
const definedHashTables = {}; |
|||
for (let i=0; i<ctx.components.length; i++) { |
|||
const {htName, htMap} = addHashTable(i); |
|||
|
|||
let code = ""; |
|||
const componentEntriesTableName = ctx.getTmpName("entryTable_" + ctx.components[i].template); |
|||
|
|||
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.join("\n") + "\n" + |
|||
"\n" + |
|||
"// Component Entries\n" + |
|||
codes_componentEntries.join("\n") + "\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 (definedHashTables[h]) return definedHashTables[h]; |
|||
definedHashTables[h] = {}; |
|||
definedHashTables[h].htName = ctx.getTmpName("ht_"+ctx.components[cIdx].template); |
|||
definedHashTables[h].htMap = []; |
|||
const t = []; |
|||
for (let i=0; i<keys.length; i++) { |
|||
definedHashTables[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]; |
|||
} |
|||
let code = `Circom_HashEntry ${definedHashTables[h].htName}[256] = {`; |
|||
for (let i=0; i<256; i++) { |
|||
code += i>0 ? "," : ""; |
|||
if (t[i]) { |
|||
code += `{0x${t[i][0]}LL, ${t[i][1]}} /* ${keys[t[i][1]]} */`; |
|||
} else { |
|||
code += "{0,0}"; |
|||
} |
|||
} |
|||
code += "};\n"; |
|||
|
|||
codes_hashMaps.push(code); |
|||
|
|||
return definedHashTables[h]; |
|||
} |
|||
} |
|||
|
|||
function buildCode(ctx) { |
|||
const globalNames = ctx.tmpNames; |
|||
|
|||
const fDefined = {}; |
|||
|
|||
const functions = []; |
|||
for (let f in ctx.functions) { |
|||
ctx.scope = {}; |
|||
const paramsList = []; |
|||
for (let p in ctx.functions[f].params) { |
|||
const param = ctx.functions[f].params[p]; |
|||
paramsList.push("POINTER "+param.name); |
|||
|
|||
ctx.scope[param.name] = { |
|||
type: "LOCAL", |
|||
sels: param.sels, |
|||
getter: () => { return param.name; }, |
|||
}; |
|||
} |
|||
|
|||
ctx.code += "void "+f+"(POINTER _ret, "+paramsList.join(",")+") {\n"; |
|||
|
|||
ctx.code += gen(ctx, ctx.functions[f].block); |
|||
ctx.code += "}"; |
|||
} |
|||
|
|||
for (let i=0; i<ctx.components.length; i++) { |
|||
const h = hashComponentCall(ctx, i); |
|||
const fName = ctx.components[i].template+"_"+h; |
|||
if (!fDefined[fName]) { |
|||
|
|||
|
|||
const scope = {_prefix : ""}; |
|||
ctx.scopes = [scope]; |
|||
ctx.nScopes = 0; |
|||
ctx.code = ""; |
|||
ctx.codeHeader = "// Header\n"; |
|||
ctx.codeFooter = "// Footer\n"; |
|||
ctx.tmpNames = Object.assign({},globalNames); |
|||
|
|||
for (let p in ctx.components[i].params) { |
|||
newRef(ctx, "BIGINT", p, ctx.components[i].params[p]); |
|||
} |
|||
|
|||
gen(ctx, ctx.templates[ctx.components[i].template].block); |
|||
|
|||
const S = `void ${fName}(Circom_CalcWit *ctx) {\n` + |
|||
utils.ident( |
|||
ctx.codeHeader + "\n" + |
|||
ctx.code + "\n" + |
|||
ctx.codeFooter |
|||
) + |
|||
"}\n"; |
|||
|
|||
functions.push(S); |
|||
} |
|||
ctx.components[i].fnName = fName; |
|||
} |
|||
|
|||
return functions.join("\n"); |
|||
} |
|||
|
|||
function buildComponentsArray(ctx) { |
|||
|
|||
const ccodes = []; |
|||
ccodes.push(`Circom_Component _components[${ctx.components.length}] = {\n`); |
|||
for (let i=0; i< ctx.components.length; i++) { |
|||
ccodes.push(i>0 ? " ," : " "); |
|||
ccodes.push(`{${ctx.components[i].htName},${ctx.components[i].etName},${ctx.components[i].fnName}, ${ctx.components[i].nInSignals}}\n`); |
|||
} |
|||
ccodes.push("};\n"); |
|||
const codeComponents = ccodes.join(""); |
|||
|
|||
return "" + |
|||
"// Components\n" + |
|||
codeComponents + |
|||
"\n"; |
|||
} |
|||
|
|||
|
|||
function buildHeader(ctx) { |
|||
return "#include \"circom.h\"\n" + |
|||
"#include \"calcwit.h\"\n" + |
|||
`#define NSignals ${ctx.signals.length}\n` + |
|||
`#define NComponents ${ctx.components.length}\n` + |
|||
`#define NInputs ${ctx.components[ ctx.getComponentIdx("main") ].nInSignals}\n`+ |
|||
`#define NOutputs ${ctx.totals[ ctx.stOUTPUT ]}\n`+ |
|||
`#define NVars ${ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL]}\n` + |
|||
"\n"; |
|||
} |
|||
|
|||
function buildSizes(ctx) { |
|||
return "// Sizes\n" + |
|||
ctx.codes_sizes.join("\n"); |
|||
} |
|||
|
|||
|
|||
function buildMapIsInput(ctx) { |
|||
const arr = []; |
|||
let line = ""; |
|||
let acc = 0; |
|||
let i; |
|||
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) { |
|||
line += (i>31) ? "," : " "; |
|||
line += toHex(acc); |
|||
acc = 0; |
|||
if ( i % (32*64) == 0) { |
|||
arr.push(line); |
|||
line = ""; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if ((i%32) != 0) { |
|||
line += (i>31) ? "," : " "; |
|||
line += toHex(acc); |
|||
} |
|||
if (line != "") { |
|||
arr.push(line); |
|||
} |
|||
|
|||
return "// mapIsArray\n" + |
|||
`u32 _mapIsInput[${Math.floor((ctx.signals.length-1) / 32)+1}] = {\n`+ |
|||
arr.join("\n") + "\n" + |
|||
"};\n"; |
|||
|
|||
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; |
|||
} |
|||
} |
|||
|
|||
|
|||
function buildWit2Sig(ctx) { |
|||
const codes = []; |
|||
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 (typeof outIdx == "undefined") continue; |
|||
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
|||
assert(outIdx<NVars); |
|||
if (typeof arr[ctx.signals[i].id] == "undefined") { |
|||
arr[outIdx] = i; |
|||
} |
|||
} |
|||
codes.push("// Signal Table\n"); |
|||
codes.push(`int _wit2sig[${NVars}] = {\n`); |
|||
let code = ""; |
|||
for (let i=0; i<NVars; i++) { |
|||
code += (i>0) ? ",": " "; |
|||
code += arr[i]; |
|||
if ((i>0)&&(i%64 == 0)) { |
|||
if (code != "") codes.push(code + "\n"); |
|||
codes.push(code); |
|||
code =0; |
|||
} |
|||
} |
|||
if (code != "") codes.push(code + "\n"); |
|||
codes.push("};\n"); |
|||
|
|||
return codes.join(""); |
|||
|
|||
} |
|||
|
|||
|
|||
function buildCircuitVar() { |
|||
return "Circom_Circuit _circuit = {\n" + |
|||
" NSignals,\n"+ |
|||
" NComponents,\n"+ |
|||
" NInputs,\n"+ |
|||
" NOutputs,\n"+ |
|||
" NVars,\n"+ |
|||
" _wit2sig,\n"+ |
|||
" _components,\n"+ |
|||
" _mapIsInput\n"+ |
|||
"};\n"; |
|||
} |
|||
|
|||
|
|||
|
|||
function hashComponentCall(ctx, cIdx) { |
|||
// TODO: At the moment generate a diferent function for each instance of the component
|
|||
return cIdx; |
|||
} |
|||
|
|||
function getTmpName(_suggestedName) { |
|||
let suggestedName; |
|||
if (_suggestedName) { |
|||
suggestedName = trimUnderscore(_suggestedName); |
|||
} else { |
|||
suggestedName = "tmp"; |
|||
} |
|||
|
|||
if (typeof(this.tmpNames[suggestedName]) == "undefined") { |
|||
this.tmpNames[suggestedName] = 1; |
|||
return "_"+suggestedName; |
|||
} else { |
|||
const name = "_" + suggestedName + "_" + this.tmpNames[suggestedName]; |
|||
this.tmpNames[suggestedName]++; |
|||
return name; |
|||
} |
|||
|
|||
function trimUnderscore(str) { |
|||
let p1=0; |
|||
while ((p1 < str.length)&&(str[p1] == "_")) p1++; |
|||
let p2=str.length; |
|||
while ((p2 > 0)&&(str[p2-1] == "_")) p2--; |
|||
|
|||
return str.slice(p1,p2); |
|||
} |
|||
|
|||
} |
|||
|
|||
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.getTmpName(name); |
|||
this.definedSizes[name] = labelName; |
|||
|
|||
const accSizes = utils.accSizes(sizes); |
|||
|
|||
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; |
|||
} |
|||
|
@ -0,0 +1,941 @@ |
|||
const bigInt = require("big-integer"); |
|||
const utils = require("./utils"); |
|||
|
|||
module.exports = gen; |
|||
|
|||
|
|||
function newRef(ctx, type, _name, value, _sizes) { |
|||
const isValue = ((typeof(value) != "undefined")&&(value != null)); |
|||
let name; |
|||
let sizes; |
|||
if (!_name) { |
|||
name = ctx.getTmpName(); |
|||
} else { |
|||
if (_name[0] =="_") { |
|||
name = ctx.getTmpName(_name); |
|||
} else { |
|||
name = _name; |
|||
} |
|||
} |
|||
if (typeof(_sizes) == "string") { |
|||
sizes = _sizes; |
|||
} else if (Array.isArray(_sizes)) { |
|||
sizes = newSizes(ctx, _sizes); |
|||
} else if (isValue) { |
|||
sizes = newSizes(ctx, utils.extractSizes(value)); |
|||
} else { |
|||
sizes = newSizes(ctx, []); |
|||
} |
|||
|
|||
const scope = ctx.scopes[ctx.scopes.length-1]; |
|||
if (scope[name]) return error("Variable already exists: " + name); |
|||
|
|||
const label = scope._prefix + name; |
|||
scope[name] = { |
|||
stack: true, |
|||
type: type, |
|||
used: false, |
|||
sizes: sizes, |
|||
label: label |
|||
}; |
|||
|
|||
if (isValue) { |
|||
scope[name].value = value; |
|||
} |
|||
|
|||
return name; |
|||
} |
|||
|
|||
function newSizes(ctx, sizes) { |
|||
const scope = ctx.scopes[ctx.scopes.length-1]; |
|||
|
|||
const name = ctx.getTmpName("_sz"); |
|||
|
|||
scope[name] = { |
|||
stack: true, |
|||
type: "SIZES", |
|||
used: false, |
|||
dim: sizes.length, |
|||
label: name, |
|||
value: sizes |
|||
}; |
|||
|
|||
return name; |
|||
} |
|||
|
|||
|
|||
function instantiateRef(ctx, name) { |
|||
const v = getScope(ctx, name); |
|||
if (!v.stack) return error("Using a non existing var: " + name); |
|||
if (v.used) return; |
|||
|
|||
if (v.type=="BIGINT") { |
|||
|
|||
const iSize = getScope(ctx, v.sizes); |
|||
|
|||
if (iSize.used) { |
|||
const labelSize = iSize.label; |
|||
ctx.codeHeader += `PBigInt ${v.label};\n`; |
|||
ctx.code += `${v.label} = ctx->allocBigInts(${labelSize});\n`; |
|||
ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; |
|||
} else if (iSize.value) { |
|||
const labelSize = ctx.addSizes(iSize.value); |
|||
ctx.codeHeader += `PBigInt ${v.label} = ctx->allocBigInts(${labelSize});\n`; |
|||
ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; |
|||
} else { |
|||
return error(ctx, null, "Undefined Sizes: " +name); |
|||
} |
|||
|
|||
} else if (v.type=="INT") { |
|||
ctx.codeHeader += `int ${v.label};\n`; |
|||
} else if (v.type=="SIZES") { |
|||
ctx.codeHeader += `Circom_Sizes ${v.label};\n`; |
|||
} |
|||
v.used = true; |
|||
} |
|||
|
|||
|
|||
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, |
|||
ast: ast, |
|||
message: errStr |
|||
}; |
|||
} |
|||
|
|||
|
|||
function getScope(ctx, name) { |
|||
for (let i=ctx.scopes.length-1; i>=0; i--) { |
|||
if (ctx.scopes[i][name]) return ctx.scopes[i][name]; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
|
|||
function gen(ctx, ast) { |
|||
if ((ast.type == "NUMBER") ) { |
|||
return genNumber(ctx, ast); |
|||
} else if (ast.type == "VARIABLE") { |
|||
return genVariable(ctx, ast); |
|||
} else if (ast.type == "PIN") { |
|||
return genPin(ctx, ast); |
|||
} else if (ast.type == "OP") { |
|||
if (ast.op == "=") { |
|||
return genVarAssignement(ctx, ast); |
|||
} else if (ast.op == "<--") { |
|||
return genVarAssignement(ctx, ast); |
|||
} else if (ast.op == "<==") { |
|||
return genSignalAssignConstrain(ctx, ast); |
|||
} else if (ast.op == "===") { |
|||
return genConstraint(ctx, ast); |
|||
} else if (ast.op == "+=") { |
|||
return genVarAddAssignement(ctx, ast); |
|||
} else if (ast.op == "*=") { |
|||
return genVarMulAssignement(ctx, ast); |
|||
} else if (ast.op == "+") { |
|||
return genAdd(ctx, ast); |
|||
} else if (ast.op == "-") { |
|||
return genSub(ctx, ast); |
|||
} else if (ast.op == "UMINUS") { |
|||
return genUMinus(ctx, ast); |
|||
} else if (ast.op == "*") { |
|||
return genMul(ctx, ast); |
|||
} else if (ast.op == "%") { |
|||
return genMod(ctx, ast); |
|||
} else if (ast.op == "PLUSPLUSRIGHT") { |
|||
return genPlusPlusRight(ctx, ast); |
|||
} else if (ast.op == "PLUSPLUSLEFT") { |
|||
return genPlusPlusLeft(ctx, ast); |
|||
} else if (ast.op == "MINUSMINUSRIGHT") { |
|||
return genMinusMinusRight(ctx, ast); |
|||
} else if (ast.op == "MINUSMINUSLEFT") { |
|||
return genMinusMinusLeft(ctx, ast); |
|||
} else if (ast.op == "**") { |
|||
return genExp(ctx, ast); |
|||
} else if (ast.op == "/") { |
|||
return genDiv(ctx, ast); |
|||
} else if (ast.op == "\\") { |
|||
return genIDiv(ctx, ast); |
|||
} else if (ast.op == "&") { |
|||
return genBAnd(ctx, ast); |
|||
} else if (ast.op == "&&") { |
|||
return genAnd(ctx, ast); |
|||
} else if (ast.op == "||") { |
|||
return genOr(ctx, ast); |
|||
} else if (ast.op == "<<") { |
|||
return genShl(ctx, ast); |
|||
} else if (ast.op == ">>") { |
|||
return genShr(ctx, ast); |
|||
} else if (ast.op == "<") { |
|||
return genLt(ctx, ast); |
|||
} else if (ast.op == ">") { |
|||
return genGt(ctx, ast); |
|||
} else if (ast.op == "<=") { |
|||
return genLte(ctx, ast); |
|||
} else if (ast.op == ">=") { |
|||
return genGte(ctx, ast); |
|||
} else if (ast.op == "==") { |
|||
return genEq(ctx, ast); |
|||
} else if (ast.op == "!=") { |
|||
return genNeq(ctx, ast); |
|||
} else if (ast.op == "?") { |
|||
return genTerCon(ctx, ast); |
|||
} else { |
|||
error(ctx, ast, "Invalid operation: " + ast.op); |
|||
} |
|||
} else if (ast.type == "DECLARE") { |
|||
if (ast.declareType == "COMPONENT") { |
|||
return genDeclareComponent(ctx, ast); |
|||
} else if ((ast.declareType == "SIGNALIN")|| |
|||
(ast.declareType == "SIGNALOUT")|| |
|||
(ast.declareType == "SIGNAL")) { |
|||
return genDeclareSignal(ctx, ast); |
|||
} else if (ast.declareType == "VARIABLE") { |
|||
return genDeclareVariable(ctx, ast); |
|||
} else { |
|||
error(ctx, ast, "Invalid declaration: " + ast.declareType); |
|||
} |
|||
} else if (ast.type == "FUNCTIONCALL") { |
|||
return genFunctionCall(ctx, ast); |
|||
} else if (ast.type == "BLOCK") { |
|||
return genBlock(ctx, ast); |
|||
} else if (ast.type == "COMPUTE") { |
|||
return genCompute(ctx, ast); |
|||
} else if (ast.type == "FOR") { |
|||
return genFor(ctx, ast); |
|||
} else if (ast.type == "WHILE") { |
|||
return genWhile(ctx, ast); |
|||
} else if (ast.type == "IF") { |
|||
return genIf(ctx, ast); |
|||
} else if (ast.type == "RETURN") { |
|||
return genReturn(ctx, ast); |
|||
} else if (ast.type == "INCLUDE") { |
|||
return genInclude(ctx, ast); |
|||
} else if (ast.type == "ARRAY") { |
|||
return genArray(ctx, ast); |
|||
} else { |
|||
error(ctx, ast, "GEN -> Invalid AST node type: " + ast.type); |
|||
} |
|||
} |
|||
|
|||
function genBlock(ctx, ast) { |
|||
const oldCode = ctx.code; |
|||
let res = null; |
|||
ctx.code = ""; |
|||
for (let i=0; i<ast.statements.length; i++) { |
|||
if (["BLOCK", "COMPUTE", "FOR", "WHILE", "IF"].indexOf(ast.statements[i].type)<0) { |
|||
genSrcComment(ctx, ast.statements[i]); |
|||
} |
|||
res = gen(ctx, ast.statements[i]); |
|||
if (ctx.error) return; |
|||
} |
|||
if (ctx.scopes.length>1) { |
|||
ctx.code = oldCode + `{\n${utils.ident(ctx.code)}}\n`; |
|||
} else { |
|||
ctx.code = oldCode + ctx.code; |
|||
} |
|||
return res; |
|||
} |
|||
|
|||
function genSrcComment(ctx, ast) { |
|||
let codes = []; |
|||
const fl= ast.first_line-1; |
|||
const ll = ast.last_line-1; |
|||
const fc = ast.first_column-1; |
|||
const lc = ast.last_column; |
|||
for (let i=fl; i<=ll; i++) { |
|||
const p1 = i==fl ? fc : 0; |
|||
const p2 = i==ll ? lc : -1; |
|||
codes.push(ctx.includedFiles[ctx.fileName][i].slice(p1, p2>=0 ? p2 : undefined)); |
|||
} |
|||
ctx.code += "\n/* "+codes.join("\n")+"*/\n"; |
|||
} |
|||
|
|||
|
|||
function genDeclareComponent(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); |
|||
|
|||
scope[ast.name.name] = { |
|||
type: "COMPONENT", |
|||
label: ast.name.name |
|||
}; |
|||
|
|||
return ast.name.name; |
|||
} |
|||
|
|||
function genDeclareSignal(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 res = { |
|||
type: "SIGNAL", |
|||
label: ast.name.name |
|||
}; |
|||
|
|||
scope[ast.name.name] = res; |
|||
|
|||
return res; |
|||
} |
|||
|
|||
function genDeclareVariable(ctx, ast) { |
|||
|
|||
const scope = ctx.scopes[ctx.scopes.length-1]; |
|||
|
|||
const varName = ast.name.name; |
|||
const labelName = scope._prefix + ast.name.name; |
|||
|
|||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); |
|||
if (ctx.scope[varName]) return error(ctx, ast, "Name already exists: "+varName); |
|||
|
|||
const sizes=[]; |
|||
let instantiate = false; |
|||
for (let i=0; i< ast.name.selectors.length; i++) { |
|||
const size = gen(ctx, ast.name.selectors[i]); |
|||
if (ctx.error) return; |
|||
|
|||
if (size.used) { |
|||
instantiate = true; |
|||
sizes.push(`BigInt2Int(${scope[size].label})`); |
|||
} else { |
|||
sizes.push(size.value.toJSNumber()); |
|||
} |
|||
|
|||
sizes.push( size.value.toJSNumber() ); |
|||
} |
|||
|
|||
const vSizes = newRef(ctx, "SIZES", "_sizes"); |
|||
const iSizes = scope[vSizes]; |
|||
iSizes.dim = ast.name.selectors.length; |
|||
if (instantiate) { |
|||
instantiateRef(ctx, vSizes); |
|||
ctx.code += `${iSizes.label}[${iSizes.dim+1}]=0`; |
|||
ctx.code += `${iSizes.label}[${iSizes.dim}]=1`; |
|||
for (let i=iSizes.dim-1; i>=0; i--) { |
|||
ctx.code += `${iSizes.label}[${i}] = ${sizes[i]}*${iSizes.label}[${i+1}];\n`; |
|||
} |
|||
} else { |
|||
iSizes.value = sizes; |
|||
} |
|||
|
|||
const res = ctx.scope[varName] = { |
|||
stack: true, |
|||
type: "BIGINT", |
|||
sizes: vSizes, |
|||
label: labelName |
|||
}; |
|||
|
|||
scope[varName] = res; |
|||
|
|||
return res; |
|||
} |
|||
|
|||
function genNumber(ctx, ast) { |
|||
return newRef(ctx, "BIGINT", "_num", ast.value); |
|||
} |
|||
|
|||
|
|||
|
|||
function genGetOffset(ctx, vOffset, vSizes, sels) { |
|||
|
|||
let rN = 0; |
|||
let rStr = ""; |
|||
let iOffset; |
|||
|
|||
if (vOffset) { |
|||
iOffset = getScope(ctx, vOffset); |
|||
if (iOffset.used) { |
|||
rStr += iOffset.label; |
|||
} else { |
|||
rN += iOffset.value.toJSNumber(); |
|||
} |
|||
} |
|||
|
|||
if ((sels)&&(sels.length>0)) { |
|||
|
|||
const iSizes = getScope(ctx, vSizes); |
|||
|
|||
for (let i=0; i<sels.length; i++) { |
|||
const vIdx = gen(ctx, sels[i]); |
|||
const iIdx = getScope(ctx, vIdx); |
|||
|
|||
if ((!iIdx.used) && (!iSizes.used)) { |
|||
rN = rN + iIdx.value * iSizes.sizes[i+1]; |
|||
} else { |
|||
if (rN>0) { |
|||
if (rStr != "") rStr += " + "; |
|||
rStr += rN; |
|||
rN =0; |
|||
} |
|||
|
|||
if (rStr != "") rStr += " + "; |
|||
if (iIdx.used) { |
|||
rStr += vIdx; |
|||
} else { |
|||
rStr += iIdx.value; |
|||
} |
|||
rStr += "*"; |
|||
if (iSizes.used) { |
|||
rStr += `${iSizes.label}[${i+1}]`; |
|||
} else { |
|||
rStr += iSizes.sizes[i+1]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (rStr == "") { |
|||
const o = newRef(ctx, "INT", "_offset", rN); |
|||
return o; |
|||
} else { |
|||
if (rN>0) { |
|||
if (rStr != "") rStr += " + "; |
|||
rStr += rN; |
|||
rN =0; |
|||
} |
|||
if (rStr == iOffset.label) { |
|||
return vOffset; |
|||
} else { |
|||
const res = newRef(ctx, "INT", "_offset"); |
|||
instantiateRef(ctx, res); |
|||
ctx.code += `${res} = ${rStr};\n`; |
|||
return res; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function genVariable(ctx, ast) { |
|||
const v = getScope(ctx, ast.name); |
|||
|
|||
|
|||
if (v.type == "SIGNAL") { |
|||
let vOffset; |
|||
if (ast.selectors.length>0) { |
|||
const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); |
|||
const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", ast.name); |
|||
vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors ); |
|||
} else { |
|||
vOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); |
|||
} |
|||
return genGetSignal(ctx, "ctx->cIdx", vOffset); |
|||
|
|||
} else if (v.type == "BIGINT") { |
|||
const vOffset = genGetOffset(ctx, 0, v.sizes, ast.sels ); |
|||
if (v.used) { |
|||
if (vOffset == 0) { |
|||
return ast.name; |
|||
} else { |
|||
const res = newRef(ctx, "BIGINT", "_v"); |
|||
instantiateRef(ctx, res); |
|||
ctx.code += `${res} = ${ast.name} + ${vOffset};\n`; |
|||
return res; |
|||
} |
|||
} else { |
|||
if (typeof(vOffset) == "string") { |
|||
instantiateRef(ctx, ast.name); |
|||
const vConstant = instantiateConstant(ctx, v.value); |
|||
const res = newRef(ctx, "BIGINT", "_v"); |
|||
instantiateRef(ctx, res); |
|||
ctx.code += `${res} = ${vConstant} + ${vOffset};\n`; |
|||
return res; |
|||
} else { |
|||
const sa = utils.subArray(v.value, ast.selectors); |
|||
const res = newRef(ctx, "BIGINT", "_v", sa); |
|||
return res; |
|||
} |
|||
} |
|||
} |
|||
|
|||
const sels = []; |
|||
for (let i=0; i<ast.selectors.length; i++) { |
|||
sels.push(gen(ctx, ast.selectors[i])); |
|||
if (ctx.error) return; |
|||
} |
|||
|
|||
if (!v) { |
|||
return error(ctx, ast, "Invalid left operand"); |
|||
} |
|||
if (v.type == "VARIABLE") { |
|||
return `ctx.getVar("${ast.name}",[${sels.join(",")}])`; |
|||
} else if (v.type == "SIGNAL") { |
|||
return `ctx.getSignal("${ast.name}", [${sels.join(",")}])`; |
|||
} else { |
|||
error(ctx, ast, "Invalid Variable type"); |
|||
} |
|||
} |
|||
|
|||
function instantiateConstant(ctx, value) { |
|||
const flatedValue = utils.flatArray(value); |
|||
const res = ctx.getTmpName("_const"); |
|||
ctx.codeHeader += `PBigInt ${res};\n`; |
|||
ctx.code += `${res.label} = ctx->allocBigInts(${flatedValue.length});\n`; |
|||
for (let i=0; i<flatedValue.length; i++) { |
|||
ctx.code += `mpz_init_set_str(${res.label}[${i}], ${flatedValue[i].toString(16)}, 16);\n`; |
|||
} |
|||
ctx.codeFooter += `ctx->freeBigInts(${res.label});\n`; |
|||
return res; |
|||
} |
|||
|
|||
function genPin(ctx, ast) { |
|||
let vcIdx; |
|||
if (ast.component.selectors.length>0) { |
|||
const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.component.name); |
|||
const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.component.name); |
|||
vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors ); |
|||
} else { |
|||
vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.component.name); |
|||
} |
|||
|
|||
let vsIdx; |
|||
if (ast.pin.selectors.length>0) { |
|||
const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.pin.name); |
|||
const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name); |
|||
vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors ); |
|||
} else { |
|||
vsIdx = genGetSigalOffset(ctx, vcIdx, ast.pin.name); |
|||
} |
|||
|
|||
return genGetSignal(ctx, vcIdx, vsIdx); |
|||
} |
|||
|
|||
function genGetSubComponentOffset(ctx, cIdx, label) { |
|||
const vOffset = newRef(ctx, "INT", "_compIdx"); |
|||
instantiateRef(ctx, vOffset); |
|||
|
|||
const h = utils.fnvHash(label); |
|||
ctx.code += `${vOffset} = ctx->getSubComponentOffset(${cIdx}, 0x${h}LL /* ${label} */);\n`; |
|||
return vOffset; |
|||
} |
|||
|
|||
function genGetSubComponentSizes(ctx, cIdx, label) { |
|||
const vSizes = newRef(ctx, "SIZES", "_compSizes"); |
|||
instantiateRef(ctx, vSizes); |
|||
|
|||
const h = utils.fnvHash(label); |
|||
ctx.code += `${vSizes} = ctx->getSubComponentSizes(${cIdx}, 0x${h}LL /* ${label} */);\n`; |
|||
return vSizes; |
|||
} |
|||
|
|||
function genGetSigalOffset(ctx, sIdx, label) { |
|||
const vOffset = newRef(ctx, "INT", "_sigIdx"); |
|||
instantiateRef(ctx, vOffset); |
|||
|
|||
const h = utils.fnvHash(label); |
|||
ctx.code += `${vOffset} = ctx->getSignalOffset(${sIdx}, 0x${h}LL /* ${label} */);\n`; |
|||
return vOffset; |
|||
} |
|||
|
|||
function genGetSignalSizes(ctx, sIdx, label) { |
|||
const vSizes = newRef(ctx, "SIZES", "_sigSizes"); |
|||
instantiateRef(ctx, vSizes); |
|||
|
|||
const h = utils.fnvHash(label); |
|||
ctx.code += `${vSizes} = ctx->getSignalSizes(${sIdx}, 0x${h}LL /* ${label} */);\n`; |
|||
return vSizes; |
|||
} |
|||
|
|||
function genSetSignal(ctx, cIdx, sIdx, value) { |
|||
ctx.code += `ctx->setSignal(${cIdx}, ${sIdx}, ${value});\n`; |
|||
return value; |
|||
} |
|||
|
|||
function genGetSignal(ctx, cIdx, sIdx) { |
|||
const res = newRef(ctx, "BIGINT", "_sigValue"); |
|||
instantiateRef(ctx, res); |
|||
ctx.code += `ctx->getSignal(${cIdx}, ${sIdx}, ${res});\n`; |
|||
return res; |
|||
} |
|||
|
|||
function genVarAssignement(ctx, ast) { |
|||
|
|||
let lName; |
|||
let sels; |
|||
|
|||
if (ctx.error) return; |
|||
|
|||
if (ast.values[0].type == "PIN") { |
|||
|
|||
let vcIdx; |
|||
if (ast.values[0].component.selectors.length>0) { |
|||
const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); |
|||
const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.values[0].component.name); |
|||
vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); |
|||
} else { |
|||
vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); |
|||
} |
|||
|
|||
let vsIdx; |
|||
if (ast.values[0].pin.selectors.length>0) { |
|||
const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); |
|||
const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); |
|||
vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); |
|||
} else { |
|||
vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); |
|||
} |
|||
|
|||
const vVal = gen(ctx, ast.values[1]); |
|||
|
|||
genSetSignal(ctx, vcIdx, vsIdx, vVal); |
|||
|
|||
return vVal; |
|||
} |
|||
|
|||
if (ast.values[0].type == "DECLARE") { |
|||
lName = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
sels = []; |
|||
} else { |
|||
lName = ast.values[0].name; |
|||
sels = ast.values[0].selectors; |
|||
} |
|||
|
|||
|
|||
const left = getScope(ctx, lName); |
|||
if (!left) return error(ctx, ast, "Variable does not exists: "+ast.values[0].name); |
|||
|
|||
// Component instantiation is already done.
|
|||
if (left.type == "COMPONENT") { |
|||
ctx.last = lName; |
|||
return; |
|||
} |
|||
|
|||
|
|||
const rName = gen(ctx, ast.values[1]); |
|||
const right = getScope(ctx, rName); |
|||
|
|||
if (left.type == "SIGNAL") { |
|||
|
|||
let vsIdx; |
|||
if (sels.length>0) { |
|||
const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", lName); |
|||
const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", lName); |
|||
vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); |
|||
} else { |
|||
vsIdx = genGetSigalOffset(ctx, "ctx->cIdx", lName); |
|||
} |
|||
|
|||
return genSetSignal(ctx, "ctx->cIdx", vsIdx, rName); |
|||
} else if (left.type == "VAR") { |
|||
if (left.used) { |
|||
if (!right.used) { |
|||
instantiateRef(ctx, rName); |
|||
} |
|||
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; |
|||
return lName; |
|||
} else { |
|||
if (right.used) { |
|||
instantiateRef(ctx, lName); |
|||
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; |
|||
return lName; |
|||
} else { |
|||
if (!left.value) { |
|||
left.value = right.value; |
|||
} else { |
|||
if (!left.value.equals(right.value)) { |
|||
if (ctx.scopes.length > 1) { |
|||
instantiateRef(ctx, lName); |
|||
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; |
|||
} else { |
|||
left.value = right.value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
return error(ctx, ast, "Assigning to invalid"); |
|||
} |
|||
} |
|||
|
|||
function genConstraint(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column; |
|||
ctx.code += `ctx->checkConstraint(${a}, ${b}, "${strErr}");`; |
|||
} |
|||
|
|||
|
|||
function genArray(ctx, ast) { |
|||
let S = "["; |
|||
for (let i=0; i<ast.values.length; i++) { |
|||
if (i>0) S += ","; |
|||
S += gen(ctx, ast.values[i]); |
|||
} |
|||
S+="]"; |
|||
return S; |
|||
} |
|||
|
|||
|
|||
function genFunctionCall(ctx, ast) { |
|||
let S = "["; |
|||
for (let i=0; i<ast.params.length; i++) { |
|||
if (i>0) S += ","; |
|||
S += gen(ctx, ast.params[i]); |
|||
} |
|||
S+="]"; |
|||
|
|||
return `ctx.callFunction("${ast.name}", ${S})`; |
|||
} |
|||
|
|||
function genFor(ctx, ast) { |
|||
ctx.scopes.push({}); |
|||
const init = gen(ctx, ast.init); |
|||
if (ctx.error) return; |
|||
const condition = gen(ctx, ast.condition); |
|||
if (ctx.error) return; |
|||
const step = gen(ctx, ast.step); |
|||
if (ctx.error) return; |
|||
const body = gen(ctx, ast.body); |
|||
if (ctx.error) return; |
|||
ctx.scopes.pop(); |
|||
return `for (${init};bigInt(${condition}).neq(bigInt(0));${step}) { \n${body}\n }\n`; |
|||
} |
|||
|
|||
function genWhile(ctx, ast) { |
|||
const condition = gen(ctx, ast.condition); |
|||
if (ctx.error) return; |
|||
const body = gen(ctx, ast.body); |
|||
if (ctx.error) return; |
|||
return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`; |
|||
} |
|||
|
|||
function genCompute(ctx, ast) { |
|||
const body = gen(ctx, ast.body); |
|||
if (ctx.error) return; |
|||
return `{\n${body}\n}\n`; |
|||
} |
|||
|
|||
function genIf(ctx, ast) { |
|||
const condition = gen(ctx, ast.condition); |
|||
if (ctx.error) return; |
|||
const thenBody = gen(ctx, ast.then); |
|||
if (ctx.error) return; |
|||
if (ast.else) { |
|||
const elseBody = gen(ctx, ast.else); |
|||
if (ctx.error) return; |
|||
return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n} else {\n${elseBody}\n}\n`; |
|||
} else { |
|||
return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n}\n`; |
|||
} |
|||
} |
|||
|
|||
|
|||
function genReturn(ctx, ast) { |
|||
const value = gen(ctx, ast.value); |
|||
if (ctx.error) return; |
|||
return `return ${value};`; |
|||
} |
|||
|
|||
|
|||
|
|||
function genSignalAssignConstrain(ctx, ast) { |
|||
const res = genVarAssignement(ctx, ast); |
|||
genConstraint(ctx, ast); |
|||
return res; |
|||
// return genVarAssignement(ctx, ast);
|
|||
} |
|||
|
|||
function genVarAddAssignement(ctx, ast) { |
|||
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); |
|||
} |
|||
|
|||
function genVarMulAssignement(ctx, ast) { |
|||
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); |
|||
} |
|||
|
|||
function genPlusPlusRight(ctx, ast) { |
|||
return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; |
|||
} |
|||
|
|||
function genPlusPlusLeft(ctx, ast) { |
|||
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); |
|||
} |
|||
|
|||
function genMinusMinusRight(ctx, ast) { |
|||
return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; |
|||
} |
|||
|
|||
function genMinusMinusLeft(ctx, ast) { |
|||
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); |
|||
} |
|||
|
|||
function genAdd(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).add(bigInt(${b})).mod(__P__)`; |
|||
} |
|||
|
|||
function genMul(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).mul(bigInt(${b})).mod(__P__)`; |
|||
} |
|||
|
|||
function genSub(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).add(__P__).sub(bigInt(${b})).mod(__P__)`; |
|||
} |
|||
|
|||
function genDiv(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
|
|||
return `bigInt(${a}).mul( bigInt(${b}).inverse(__P__) ).mod(__P__)`; |
|||
} |
|||
|
|||
function genIDiv(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
|
|||
return `bigInt(${a}).div( bigInt(${b}))`; |
|||
} |
|||
|
|||
function genExp(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).modPow(bigInt(${b}), __P__)`; |
|||
} |
|||
|
|||
function genBAnd(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`; |
|||
} |
|||
|
|||
function genAnd(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; |
|||
} |
|||
|
|||
function genOr(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; |
|||
} |
|||
|
|||
function genShl(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`; |
|||
} |
|||
|
|||
function genShr(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`; |
|||
} |
|||
|
|||
function genMod(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).mod(bigInt(${b}))`; |
|||
} |
|||
|
|||
function genLt(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`; |
|||
} |
|||
|
|||
function genGt(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).gt(bigInt(${b})) ? 1 : 0`; |
|||
} |
|||
|
|||
function genLte(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).lesserOrEquals(bigInt(${b})) ? 1 : 0`; |
|||
} |
|||
|
|||
function genGte(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).greaterOrEquals(bigInt(${b})) ? 1 : 0`; |
|||
} |
|||
|
|||
function genEq(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `(bigInt(${a}).eq(bigInt(${b})) ? 1 : 0)`; |
|||
} |
|||
|
|||
function genNeq(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
return `(bigInt(${a}).eq(bigInt(${b})) ? 0 : 1)`; |
|||
} |
|||
|
|||
function genUMinus(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
return `__P__.sub(bigInt(${a})).mod(__P__)`; |
|||
} |
|||
|
|||
function genTerCon(ctx, ast) { |
|||
const a = gen(ctx, ast.values[0]); |
|||
if (ctx.error) return; |
|||
const b = gen(ctx, ast.values[1]); |
|||
if (ctx.error) return; |
|||
const c = gen(ctx, ast.values[2]); |
|||
if (ctx.error) return; |
|||
return `bigInt(${a}).neq(bigInt(0)) ? (${b}) : (${c})`; |
|||
} |
|||
|
|||
function genInclude(ctx, ast) { |
|||
return ast.block ? gen(ctx, ast.block) : ""; |
|||
} |
|||
|
|||
|
@ -0,0 +1,119 @@ |
|||
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("snarkjs").stringifyBigInts; |
|||
const unstringifyBigInts = require("snarkjs").unstringifyBigInts; |
|||
const bigInt = require("snarkjs").bigInt; |
|||
|
|||
module.exports = c_tester; |
|||
|
|||
|
|||
async function c_tester(circomFile, mainComponent, _options) { |
|||
tmp.setGracefulCleanup(); |
|||
mainComponent = mainComponent || "main"; |
|||
|
|||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true }); |
|||
|
|||
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.mainComponent = mainComponent; |
|||
await compiler(circomFile, options); |
|||
|
|||
const cdir = path.join(__dirname, "..", "c"); |
|||
await exec("g++" + |
|||
` ${path.join(dir.path, baseName + ".cpp")} ` + |
|||
` ${path.join(cdir, "main.cpp")}` + |
|||
` ${path.join(cdir, "calcwit.cpp")}` + |
|||
` ${path.join(cdir, "utils.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); |
|||
} |
|||
|
|||
class CTester { |
|||
|
|||
constructor(dir, baseName, mainComponent) { |
|||
this.dir=dir; |
|||
this.baseName = baseName; |
|||
this.mainComponent = mainComponent; |
|||
} |
|||
|
|||
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() { |
|||
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 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") { |
|||
for (let k in eOut) { |
|||
checkObject(prefix + "."+k, eOut[k]); |
|||
} |
|||
} else { |
|||
const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString(); |
|||
const be = bigInt(eOut).toString(); |
|||
assert.strictEqual(ba, be, prefix); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
|
@ -0,0 +1,171 @@ |
|||
const bigInt = require("big-integer"); |
|||
|
|||
|
|||
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 = []; |
|||
|
|||
this.currentComponent= -1; |
|||
this.constraints= []; |
|||
this.components= []; |
|||
this.templates= {}; |
|||
this.functions= {}; |
|||
this.functionParams= {}; |
|||
this.nSignals = 0; |
|||
this.nComponents =0; |
|||
this.names = new TableName(this); |
|||
this.main=false; |
|||
|
|||
const oneIdx = this.addSignal("one"); |
|||
this.signals[oneIdx] = { |
|||
v: bigInt(1), |
|||
o: this.ONE, |
|||
e: -1, |
|||
}; |
|||
} |
|||
|
|||
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); |
|||
} |
|||
|
|||
}; |
@ -0,0 +1,55 @@ |
|||
const fnv = require("fnv-plus"); |
|||
|
|||
module.exports.ident =ident; |
|||
|
|||
module.exports.extractSizes =extractSizes; |
|||
module.exports.csArr = csArr; |
|||
module.exports.subArray = subArray; |
|||
module.exports.accSizes = accSizes; |
|||
module.exports.fnvHash = fnvHash; |
|||
|
|||
function ident(text) { |
|||
let lines = text.split("\n"); |
|||
for (let i=0; i<lines.length; i++) { |
|||
if (lines[i]) lines[i] = " "+lines[i]; |
|||
} |
|||
return lines.join("\n"); |
|||
} |
|||
|
|||
function extractSizes (o) { |
|||
if (! Array.isArray(o)) return [1, 0]; |
|||
return [o.length, ...extractSizes(o[0])]; |
|||
} |
|||
|
|||
// 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 subArray(value, sels) { |
|||
if ((!sels) || (sels.length == 0)) return value; |
|||
|
|||
return subArray(value[sels[0]], sels.slice(1)); |
|||
} |
|||
|
|||
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(); |
|||
} |
|||
|
|||
|
|||
|
@ -0,0 +1 @@ |
|||
|
@ -0,0 +1 @@ |
|||
{ "in1": 1, "in2": [2,3], "in3":[[4,5],[6,7],[8,9]] } |
@ -1,18 +1,54 @@ |
|||
template Internal() { |
|||
signal input in; |
|||
signal output out; |
|||
signal input in1; |
|||
signal input in2[2]; |
|||
signal input in3[3][2]; |
|||
|
|||
out <== in; |
|||
signal output out1; |
|||
signal output out2[2]; |
|||
signal output out3[3][2]; |
|||
|
|||
out1 <== in1; |
|||
out2[0] <== in2[0]; |
|||
out2[1] <== in2[1]; |
|||
|
|||
out3[0][0] <== in3[0][0]; |
|||
out3[0][1] <== in3[0][1]; |
|||
out3[1][0] <== in3[1][0]; |
|||
out3[1][1] <== in3[1][1]; |
|||
out3[2][0] <== in3[2][0]; |
|||
out3[2][1] <== in3[2][1]; |
|||
} |
|||
|
|||
template InOut() { |
|||
signal input in; |
|||
signal output out; |
|||
signal input in1; |
|||
signal input in2[2]; |
|||
signal input in3[3][2]; |
|||
|
|||
signal output out1; |
|||
signal output out2[2]; |
|||
signal output out3[3][2]; |
|||
|
|||
component internal = Internal(); |
|||
|
|||
internal.in <== in; |
|||
internal.out ==> out; |
|||
internal.in1 <== in1; |
|||
internal.in2[0] <== in2[0]; |
|||
internal.in2[1] <== in2[1]; |
|||
internal.in3[0][0] <== in3[0][0]; |
|||
internal.in3[0][1] <== in3[0][1]; |
|||
internal.in3[1][0] <== in3[1][0]; |
|||
internal.in3[1][1] <== in3[1][1]; |
|||
internal.in3[2][0] <== in3[2][0]; |
|||
internal.in3[2][1] <== in3[2][1]; |
|||
|
|||
internal.out1 ==> out1; |
|||
internal.out2[0] ==> out2[0]; |
|||
internal.out2[1] ==> out2[1]; |
|||
internal.out3[0][0] ==> out3[0][0]; |
|||
internal.out3[0][1] ==> out3[0][1]; |
|||
internal.out3[1][0] ==> out3[1][0]; |
|||
internal.out3[1][1] ==> out3[1][1]; |
|||
internal.out3[2][0] ==> out3[2][0]; |
|||
internal.out3[2][1] ==> out3[2][1]; |
|||
} |
|||
|
|||
component main = InOut(); |
@ -0,0 +1,20 @@ |
|||
const chai = require("chai"); |
|||
const path = require("path"); |
|||
|
|||
const c_tester = require("../index.js").c_tester; |
|||
const stringifyBigInts = require("snarkjs").stringifyBigInts; |
|||
|
|||
|
|||
describe("inout test", function () { |
|||
this.timeout(100000); |
|||
it("Should compile a code with vars inside a for", async () => { |
|||
const cir = await c_tester(path.join(__dirname, "circuits", "inout.circom")); |
|||
|
|||
const out = await cir.calculateWitness({in1: 1, in2: [2,3], in3:[[4,5], [6,7], [8,9]]}); |
|||
|
|||
// console.log(JSON.stringify(stringifyBigInts(out),null,1));
|
|||
await cir.assertOut(out, {out1: 1, out2: [2,3], out3: [[4,5], [6,7],[8,9]]} ); |
|||
|
|||
await cir.release(); |
|||
}); |
|||
}); |