mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-06 18:56:40 +01:00
C generation
This commit is contained in:
155
c/calcwit.cpp
Normal file
155
c/calcwit.cpp
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
61
c/calcwit.h
Normal file
61
c/calcwit.h
Normal file
@@ -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
|
||||||
56
c/circom.h
Normal file
56
c/circom.h
Normal file
@@ -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
|
||||||
196
c/main.cpp
Normal file
196
c/main.cpp
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
c/mainjson
Normal file
BIN
c/mainjson
Normal file
Binary file not shown.
47
c/mainjson.cpp
Normal file
47
c/mainjson.cpp
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
25
c/utils.cpp
Normal file
25
c/utils.cpp
Normal file
@@ -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;
|
||||||
|
}
|
||||||
10
c/utils.h
Normal file
10
c/utils.h
Normal file
@@ -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
|
||||||
49
cli.js
49
cli.js
@@ -30,8 +30,11 @@ const version = require("./package").version;
|
|||||||
|
|
||||||
const argv = require("yargs")
|
const argv = require("yargs")
|
||||||
.version(version)
|
.version(version)
|
||||||
.usage("circom [input source circuit file] -o [output definition circuit file]")
|
.usage("circom [input source circuit file] -o [output definition circuit file] -c [output c file]")
|
||||||
.alias("o", "output")
|
.alias("o", "output")
|
||||||
|
.alias("c", "csource")
|
||||||
|
.alias("s", "sym")
|
||||||
|
.alias("r", "r1cs")
|
||||||
.help("h")
|
.help("h")
|
||||||
.alias("h", "help")
|
.alias("h", "help")
|
||||||
.option("verbose", {
|
.option("verbose", {
|
||||||
@@ -63,11 +66,49 @@ if (argv._.length == 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fullFileName = path.resolve(process.cwd(), inputFile);
|
const fullFileName = path.resolve(process.cwd(), inputFile);
|
||||||
const outName = argv.output ? argv.output : "circuit.json";
|
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";
|
||||||
|
|
||||||
compiler(fullFileName, {reduceConstraints: !argv.fast, verbose: argv.verbose}).then( (cir) => {
|
const options = {};
|
||||||
fs.writeFileSync(outName, JSON.stringify(cir, null, 1), "utf8");
|
options.reduceConstraints = !argv.fast;
|
||||||
|
options.verbose = argv.verbose || false;
|
||||||
|
if (argv.csource) {
|
||||||
|
options.cSourceWriteStream = fs.createWriteStream(cSourceName);
|
||||||
|
}
|
||||||
|
if (argv.r1cs) {
|
||||||
|
options.r1csWriteStream = fs.createWriteStream(r1csName);
|
||||||
|
}
|
||||||
|
if (argv.sym) {
|
||||||
|
options.symWriteStream = fs.createWriteStream(symName);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler(fullFileName, options).then( () => {
|
||||||
|
let r1csDone = false;
|
||||||
|
let cSourceDone = false;
|
||||||
|
if (options.r1csWriteStream) {
|
||||||
|
options.r1csWriteStream.end(() => {
|
||||||
|
r1csDone = true;
|
||||||
|
finishIfDone();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
r1csDone = true;
|
||||||
|
}
|
||||||
|
if (options.cSourceWriteStream) {
|
||||||
|
options.cSourceWriteStream.end(() => {
|
||||||
|
cSourceDone = true;
|
||||||
|
finishIfDone();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cSourceDone = true;
|
||||||
|
}
|
||||||
|
function finishIfDone() {
|
||||||
|
if ((r1csDone)&&(cSourceDone)) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
// console.log(err);
|
// console.log(err);
|
||||||
console.log(err.stack);
|
console.log(err.stack);
|
||||||
|
|||||||
BIN
doc/lc_example.monopic
Normal file
BIN
doc/lc_example.monopic
Normal file
Binary file not shown.
489
doc/r1cs_bin_format.md
Normal file
489
doc/r1cs_bin_format.md
Normal file
@@ -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/).
|
||||||
BIN
doc/r1cs_bin_format.monopic
Normal file
BIN
doc/r1cs_bin_format.monopic
Normal file
Binary file not shown.
BIN
doc/r1cs_example.monopic
Normal file
BIN
doc/r1cs_example.monopic
Normal file
Binary file not shown.
3
index.js
3
index.js
@@ -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");
|
||||||
|
|||||||
51
package-lock.json
generated
51
package-lock.json
generated
@@ -111,8 +111,7 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"big-integer": {
|
"big-integer": {
|
||||||
"version": "1.6.43",
|
"version": "1.6.43",
|
||||||
@@ -132,7 +131,6 @@
|
|||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -249,8 +247,7 @@
|
|||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
"version": "6.0.5",
|
"version": "6.0.5",
|
||||||
@@ -585,11 +582,15 @@
|
|||||||
"integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
|
"integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fnv-plus": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw=="
|
||||||
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"functional-red-black-tree": {
|
"functional-red-black-tree": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -620,7 +621,6 @@
|
|||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -677,7 +677,6 @@
|
|||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.3.0",
|
"once": "^1.3.0",
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
@@ -686,8 +685,7 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"inquirer": {
|
"inquirer": {
|
||||||
"version": "6.2.2",
|
"version": "6.2.2",
|
||||||
@@ -911,7 +909,6 @@
|
|||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@@ -1107,8 +1104,7 @@
|
|||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"path-is-inside": {
|
"path-is-inside": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -1196,11 +1192,18 @@
|
|||||||
"version": "2.6.3",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
||||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rimraf-promise": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf-promise/-/rimraf-promise-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-PdvkN4wa3slmvDZt37yYUUPHaVI=",
|
||||||
|
"requires": {
|
||||||
|
"rimraf": "^2.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"run-async": {
|
"run-async": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
|
||||||
@@ -1405,6 +1408,24 @@
|
|||||||
"os-tmpdir": "~1.0.2"
|
"os-tmpdir": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tmp-promise": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-zl71nFWjPKW2KXs+73gEk8RmqvtAeXPxhWDkTUoa3MSMkjq3I+9OeknjF178MQoMYsdqL730hfzvNfEkePxq9Q==",
|
||||||
|
"requires": {
|
||||||
|
"tmp": "0.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tmp": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
|
||||||
|
"requires": {
|
||||||
|
"rimraf": "^2.6.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||||
|
|||||||
@@ -30,7 +30,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"big-integer": "^1.6.32",
|
"big-integer": "^1.6.32",
|
||||||
|
"fnv-plus": "^1.3.1",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
|
"rimraf-promise": "^2.0.0",
|
||||||
|
"tmp-promise": "^2.0.2",
|
||||||
"yargs": "^12.0.2"
|
"yargs": "^12.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
52
src/buildwasm.js
Normal file
52
src/buildwasm.js
Normal file
@@ -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) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
377
src/c_build.js
Normal file
377
src/c_build.js
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
941
src/c_gen.js
Normal file
941
src/c_gen.js
Normal file
@@ -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) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
119
src/c_tester.js
Normal file
119
src/c_tester.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
302
src/compiler.js
302
src/compiler.js
@@ -22,10 +22,12 @@ const path = require("path");
|
|||||||
const bigInt = require("big-integer");
|
const bigInt = require("big-integer");
|
||||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||||
|
const sONE = 0;
|
||||||
const assert = require("assert");
|
const assert = require("assert");
|
||||||
const gen = require("./gencode");
|
const buildC = require("./c_build");
|
||||||
const exec = require("./exec");
|
const exec = require("./exec");
|
||||||
const lc = require("./lcalgebra");
|
const lc = require("./lcalgebra");
|
||||||
|
const Ctx = require("./ctx");
|
||||||
|
|
||||||
module.exports = compile;
|
module.exports = compile;
|
||||||
|
|
||||||
@@ -48,41 +50,34 @@ async function compile(srcFile, options) {
|
|||||||
|
|
||||||
assert(ast.type == "BLOCK");
|
assert(ast.type == "BLOCK");
|
||||||
|
|
||||||
const ctx = {
|
const ctx = new Ctx();
|
||||||
scopes: [{}],
|
ctx.mainComponent = options.mainComponent || "main";
|
||||||
signals: {
|
ctx.filePath= fullFilePath;
|
||||||
one: {
|
ctx.fileName= fullFileName;
|
||||||
fullName: "one",
|
ctx.includedFiles = {};
|
||||||
value: bigInt(1),
|
ctx.includedFiles[fullFileName] = src.split("\n");
|
||||||
equivalence: "",
|
|
||||||
direction: ""
|
ctx.verbose= options.verbose || false;
|
||||||
}
|
|
||||||
},
|
|
||||||
currentComponent: "",
|
|
||||||
constraints: [],
|
|
||||||
components: {},
|
|
||||||
templates: {},
|
|
||||||
functions: {},
|
|
||||||
functionParams: {},
|
|
||||||
filePath: fullFilePath,
|
|
||||||
fileName: fullFileName,
|
|
||||||
verbose: options.verbose || false
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
exec(ctx, ast);
|
exec(ctx, ast);
|
||||||
|
|
||||||
if (!ctx.components["main"]) {
|
if (ctx.error) {
|
||||||
|
throw(ctx.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.getComponentIdx(ctx.mainComponent)<0) {
|
||||||
throw new Error("A main component must be defined");
|
throw new Error("A main component must be defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.verbose) console.log("Classify Signals");
|
if (ctx.verbose) console.log("Classify Signals");
|
||||||
classifySignals(ctx);
|
classifySignals(ctx);
|
||||||
|
|
||||||
if (ctx.verbose) console.log("Reduce Constraints");
|
if (ctx.verbose) console.log("Reduce Constants");
|
||||||
reduceConstants(ctx);
|
reduceConstants(ctx);
|
||||||
if (options.reduceConstraints) {
|
if (options.reduceConstraints) {
|
||||||
|
|
||||||
|
if (ctx.verbose) console.log("Reduce Constraints");
|
||||||
// Repeat while reductions are performed
|
// Repeat while reductions are performed
|
||||||
let oldNConstrains = -1;
|
let oldNConstrains = -1;
|
||||||
while (ctx.constraints.length != oldNConstrains) {
|
while (ctx.constraints.length != oldNConstrains) {
|
||||||
@@ -97,121 +92,134 @@ async function compile(srcFile, options) {
|
|||||||
throw(ctx.error);
|
throw(ctx.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.scopes = [{}];
|
|
||||||
|
|
||||||
const mainCode = gen(ctx,ast);
|
if (options.cSourceWriteStream) {
|
||||||
|
const cSrc = buildC(ctx);
|
||||||
|
options.cSourceWriteStream.write(cSrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const mainCode = gen(ctx,ast);
|
||||||
if (ctx.error) throw(ctx.error);
|
if (ctx.error) throw(ctx.error);
|
||||||
|
|
||||||
const def = buildCircuitDef(ctx, mainCode);
|
if (options.r1csWriteStream) {
|
||||||
|
buildR1cs(ctx, options.r1csWriteStream);
|
||||||
return def;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.symWriteStream) {
|
||||||
|
buildSyms(ctx, options.symWriteStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const def = buildCircuitDef(ctx, mainCode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function classifySignals(ctx) {
|
function classifySignals(ctx) {
|
||||||
|
|
||||||
|
const ERROR = 0xFFFF;
|
||||||
|
|
||||||
function priorize(t1, t2) {
|
function priorize(t1, t2) {
|
||||||
if ((t1 == "error") || (t2=="error")) return "error";
|
if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
|
||||||
if (t1 == "internal") {
|
if (t1 == ctx.stINTERNAL) {
|
||||||
return t2;
|
return t2;
|
||||||
} else if (t2=="internal") {
|
} else if (t2==ctx.stINTERNAL) {
|
||||||
return t1;
|
return t1;
|
||||||
}
|
}
|
||||||
if ((t1 == "one") || (t2 == "one")) return "one";
|
if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
|
||||||
if ((t1 == "constant") || (t2 == "constant")) return "constant";
|
if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
|
||||||
if (t1!=t2) return "error";
|
if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
|
||||||
|
if (t1!=t2) return ERROR;
|
||||||
return t1;
|
return t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First classify the signals
|
// First classify the signals
|
||||||
for (let s in ctx.signals) {
|
for (let s in ctx.signals) {
|
||||||
const signal = ctx.signals[s];
|
const signal = ctx.signals[s];
|
||||||
let tAll = "internal";
|
let tAll = ctx.stINTERNAL;
|
||||||
let lSignal = signal;
|
let lSignal = signal;
|
||||||
let end = false;
|
let end = false;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
let t = lSignal.category || "internal";
|
let t = lSignal.c || ctx.stINTERNAL;
|
||||||
if (s == "one") {
|
if (s == 0) {
|
||||||
t = "one";
|
t = ctx.stONE;
|
||||||
} else if (lSignal.value) {
|
} else if (lSignal.v) {
|
||||||
t = "constant";
|
t = ctx.stCONSTANT;
|
||||||
} else if (lSignal.component=="main") {
|
} else if (lSignal.o & ctx.MAIN) {
|
||||||
if (lSignal.direction == "IN") {
|
if (lSignal.o & ctx.IN) {
|
||||||
if (lSignal.private) {
|
if (lSignal.o & ctx.PRV) {
|
||||||
t = "prvInput";
|
t = ctx.stPRVINPUT;
|
||||||
} else {
|
} else {
|
||||||
t = "pubInput";
|
t = ctx.stPUBINPUT;
|
||||||
}
|
}
|
||||||
} else if (lSignal.direction == "OUT") {
|
} else if (lSignal.o & ctx.OUT) {
|
||||||
t = "output";
|
t = ctx.stOUTPUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tAll = priorize(t,tAll);
|
tAll = priorize(t,tAll);
|
||||||
if (lSignal.equivalence) {
|
if (lSignal.e>=0) {
|
||||||
lSignal = ctx.signals[lSignal.equivalence];
|
lSignal = ctx.signals[lSignal.e];
|
||||||
} else {
|
} else {
|
||||||
end=true;
|
end=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tAll == "error") {
|
if (tAll == ERROR) {
|
||||||
throw new Error("Incompatible types in signal: " + s);
|
throw new Error("Incompatible types in signal: " + s);
|
||||||
}
|
}
|
||||||
lSignal.category = tAll;
|
lSignal.c = tAll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function generateWitnessNames(ctx) {
|
function generateWitnessNames(ctx) {
|
||||||
|
const totals = {};
|
||||||
const totals = {
|
totals[ctx.stONE] = 0;
|
||||||
"output": 0,
|
totals[ctx.stOUTPUT] = 0;
|
||||||
"pubInput": 0,
|
totals[ctx.stPUBINPUT] = 0;
|
||||||
"one": 0,
|
totals[ctx.stPRVINPUT] = 0;
|
||||||
"prvInput": 0,
|
totals[ctx.stINTERNAL] = 0;
|
||||||
"internal": 0,
|
totals[ctx.stDISCARDED] = 0;
|
||||||
"constant": 0,
|
totals[ctx.stCONSTANT] = 0;
|
||||||
};
|
|
||||||
const ids = {};
|
const ids = {};
|
||||||
|
|
||||||
const counted = {};
|
|
||||||
|
|
||||||
// First classify the signals
|
// First classify the signals
|
||||||
for (let s in ctx.signals) {
|
for (let s=0; s<ctx.signals.length; s++) {
|
||||||
|
|
||||||
|
if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
|
||||||
|
|
||||||
const signal = ctx.signals[s];
|
const signal = ctx.signals[s];
|
||||||
let lSignal = signal;
|
let lSignal = signal;
|
||||||
while (lSignal.equivalence) lSignal = ctx.signals[lSignal.equivalence];
|
while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
|
||||||
|
|
||||||
if (!counted[lSignal.fullName]) {
|
if (!( lSignal.o & ctx.COUNTED) ) {
|
||||||
counted[lSignal.fullName] = true;
|
lSignal.o |= ctx.COUNTED;
|
||||||
totals[lSignal.category] ++;
|
totals[lSignal.c] ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ids["one"] = 0;
|
ids[ctx.stONE] = 0;
|
||||||
ids["output"] = 1;
|
ids[ctx.stOUTPUT] = 1;
|
||||||
ids["pubInput"] = ids["output"] + totals["output"];
|
ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
|
||||||
ids["prvInput"] = ids["pubInput"] + totals["pubInput"];
|
ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
|
||||||
ids["internal"] = ids["prvInput"] + totals["prvInput"];
|
ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
|
||||||
ids["constant"] = ids["internal"] + totals["internal"];
|
ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
|
||||||
const nSignals = ids["constant"] + totals["constant"];
|
ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
|
||||||
|
const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
|
||||||
|
|
||||||
ctx.signalNames = new Array(nSignals);
|
for (let s=0; s<ctx.signals.length; s++) {
|
||||||
for (let i=0; i< nSignals; i++) ctx.signalNames[i] = [];
|
|
||||||
ctx.signalName2Idx = {};
|
if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
|
||||||
|
|
||||||
for (let s in ctx.signals) {
|
|
||||||
const signal = ctx.signals[s];
|
const signal = ctx.signals[s];
|
||||||
let lSignal = signal;
|
let lSignal = signal;
|
||||||
while (lSignal.equivalence) {
|
while (lSignal.e>=0) {
|
||||||
lSignal = ctx.signals[lSignal.equivalence];
|
lSignal = ctx.signals[lSignal.e];
|
||||||
}
|
}
|
||||||
if ( typeof(lSignal.id) === "undefined" ) {
|
if ( typeof(lSignal.id) === "undefined" ) {
|
||||||
lSignal.id = ids[lSignal.category] ++;
|
lSignal.id = ids[lSignal.c] ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
signal.id = lSignal.id;
|
signal.id = lSignal.id;
|
||||||
ctx.signalNames[signal.id].push(signal.fullName);
|
|
||||||
ctx.signalName2Idx[signal.fullName] = signal.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.totals = totals;
|
ctx.totals = totals;
|
||||||
@@ -225,6 +233,7 @@ function reduceConstants(ctx) {
|
|||||||
if (!lc.isZero(c)) {
|
if (!lc.isZero(c)) {
|
||||||
newConstraints.push(c);
|
newConstraints.push(c);
|
||||||
}
|
}
|
||||||
|
delete ctx.constraints[i];
|
||||||
}
|
}
|
||||||
ctx.constraints = newConstraints;
|
ctx.constraints = newConstraints;
|
||||||
}
|
}
|
||||||
@@ -232,10 +241,12 @@ function reduceConstants(ctx) {
|
|||||||
function reduceConstrains(ctx) {
|
function reduceConstrains(ctx) {
|
||||||
indexVariables();
|
indexVariables();
|
||||||
let possibleConstraints = Object.keys(ctx.constraints);
|
let possibleConstraints = Object.keys(ctx.constraints);
|
||||||
|
let ii=0;
|
||||||
while (possibleConstraints.length>0) {
|
while (possibleConstraints.length>0) {
|
||||||
let nextPossibleConstraints = {};
|
let nextPossibleConstraints = {};
|
||||||
for (let i in possibleConstraints) {
|
for (let i in possibleConstraints) {
|
||||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constraints: ", i);
|
ii++;
|
||||||
|
if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
|
||||||
if (!ctx.constraints[i]) continue;
|
if (!ctx.constraints[i]) continue;
|
||||||
const c = ctx.constraints[i];
|
const c = ctx.constraints[i];
|
||||||
|
|
||||||
@@ -248,13 +259,13 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
// Mov to C if possible.
|
// Mov to C if possible.
|
||||||
if (isConstant(c.a)) {
|
if (isConstant(c.a)) {
|
||||||
const ct = {type: "NUMBER", value: c.a.values["one"]};
|
const ct = {type: "NUMBER", value: c.a.values[sONE]};
|
||||||
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
||||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||||
}
|
}
|
||||||
if (isConstant(c.b)) {
|
if (isConstant(c.b)) {
|
||||||
const ct = {type: "NUMBER", value: c.b.values["one"]};
|
const ct = {type: "NUMBER", value: c.b.values[sONE]};
|
||||||
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
||||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||||
@@ -265,8 +276,8 @@ function reduceConstrains(ctx) {
|
|||||||
if (isolatedSignal) {
|
if (isolatedSignal) {
|
||||||
|
|
||||||
let lSignal = ctx.signals[isolatedSignal];
|
let lSignal = ctx.signals[isolatedSignal];
|
||||||
while (lSignal.equivalence) {
|
while (lSignal.e>=0) {
|
||||||
lSignal = ctx.signals[lSignal.equivalence];
|
lSignal = ctx.signals[lSignal.e];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -296,7 +307,7 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
ctx.constraints[i] = null;
|
ctx.constraints[i] = null;
|
||||||
|
|
||||||
lSignal.category = "constant";
|
lSignal.c = ctx.stDISCARDED;
|
||||||
} else {
|
} else {
|
||||||
if (lc.isZero(c.c)) ctx.constraints[i] = null;
|
if (lc.isZero(c.c)) ctx.constraints[i] = null;
|
||||||
}
|
}
|
||||||
@@ -332,8 +343,8 @@ function reduceConstrains(ctx) {
|
|||||||
function unindexVariables() {
|
function unindexVariables() {
|
||||||
for (let s in ctx.signals) {
|
for (let s in ctx.signals) {
|
||||||
let lSignal = ctx.signals[s];
|
let lSignal = ctx.signals[s];
|
||||||
while (lSignal.equivalence) {
|
while (lSignal.e>=0) {
|
||||||
lSignal = ctx.signals[lSignal.equivalence];
|
lSignal = ctx.signals[lSignal.e];
|
||||||
}
|
}
|
||||||
if (lSignal.inConstraints) delete lSignal.inConstraints;
|
if (lSignal.inConstraints) delete lSignal.inConstraints;
|
||||||
}
|
}
|
||||||
@@ -342,8 +353,8 @@ function reduceConstrains(ctx) {
|
|||||||
/*
|
/*
|
||||||
function unlinkSignal(signalName, cidx) {
|
function unlinkSignal(signalName, cidx) {
|
||||||
let lSignal = ctx.signals[signalName];
|
let lSignal = ctx.signals[signalName];
|
||||||
while (lSignal.equivalence) {
|
while (lSignal.e>=0) {
|
||||||
lSignal = ctx.signals[lSignal.equivalence];
|
lSignal = ctx.signals[lSignal.e];
|
||||||
}
|
}
|
||||||
if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
|
if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
|
||||||
delete lSignal.inConstraints[cidx];
|
delete lSignal.inConstraints[cidx];
|
||||||
@@ -353,8 +364,8 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
function linkSignal(signalName, cidx) {
|
function linkSignal(signalName, cidx) {
|
||||||
let lSignal = ctx.signals[signalName];
|
let lSignal = ctx.signals[signalName];
|
||||||
while (lSignal.equivalence) {
|
while (lSignal.e>=0) {
|
||||||
lSignal = ctx.signals[lSignal.equivalence];
|
lSignal = ctx.signals[lSignal.e];
|
||||||
}
|
}
|
||||||
if (!lSignal.inConstraints) lSignal.inConstraints = {};
|
if (!lSignal.inConstraints) lSignal.inConstraints = {};
|
||||||
lSignal.inConstraints[cidx] = true;
|
lSignal.inConstraints[cidx] = true;
|
||||||
@@ -363,21 +374,22 @@ function reduceConstrains(ctx) {
|
|||||||
function getFirstInternalSignal(ctx, l) {
|
function getFirstInternalSignal(ctx, l) {
|
||||||
for (let k in l.values) {
|
for (let k in l.values) {
|
||||||
const signal = ctx.signals[k];
|
const signal = ctx.signals[k];
|
||||||
if (signal.category == "internal") return k;
|
if (signal.c == ctx.stINTERNAL) return k;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isConstant(l) {
|
function isConstant(l) {
|
||||||
for (let k in l.values) {
|
for (let k in l.values) {
|
||||||
if ((k != "one") && (!l.values[k].isZero())) return false;
|
if ((k != sONE) && (!l.values[k].isZero())) return false;
|
||||||
}
|
}
|
||||||
if (!l.values["one"] || l.values["one"].isZero()) return false;
|
if (!l.values[sONE] || l.values[sONE].isZero()) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
function buildCircuitDef(ctx, mainCode) {
|
function buildCircuitDef(ctx, mainCode) {
|
||||||
const res = {
|
const res = {
|
||||||
@@ -436,6 +448,9 @@ function buildCircuitDef(ctx, mainCode) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Build constraints
|
Build constraints
|
||||||
|
|
||||||
@@ -485,5 +500,96 @@ function buildConstraints(ctx) {
|
|||||||
return res;
|
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) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
|
||||||
|
|
||||||
|
|
||||||
|
function addSymbolsComponent(prefix, idComponet) {
|
||||||
|
for (let n in ctx.components[idComponet].names.o) {
|
||||||
|
const entrie = ctx.components[idComponet].names.o[n];
|
||||||
|
addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSymbolArray(prefix, type, sizes, offset) {
|
||||||
|
if (sizes.length==0) {
|
||||||
|
if (type == "S") {
|
||||||
|
let s=offset;
|
||||||
|
while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
|
||||||
|
let wId = ctx.signals[s].id;
|
||||||
|
if (typeof(wId) == "undefined") wId=-1;
|
||||||
|
strm.write(`${offset},${wId},${prefix}\n`);
|
||||||
|
} else {
|
||||||
|
addSymbolsComponent(prefix+".", offset);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
let acc = 0;
|
||||||
|
for (let i=0; i<sizes[0]; i++) {
|
||||||
|
acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
171
src/ctx.js
Normal file
171
src/ctx.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
215
src/exec.js
215
src/exec.js
@@ -214,16 +214,38 @@ function getScope(ctx, name, selectors) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function select(v, s) {
|
function select(v, sels) {
|
||||||
s = s || [];
|
if (v.type == "SIGNAL") {
|
||||||
|
return reduce(v, sels, "sIdx");
|
||||||
|
} else if (v.type == "COMPONENT") {
|
||||||
|
return reduce(v, sels, "cIdx");
|
||||||
|
} else {
|
||||||
|
const s = sels || [];
|
||||||
if (s.length == 0) return v;
|
if (s.length == 0) return v;
|
||||||
return select(v[s[0]], s.slice(1));
|
return select(v[s[0]], s.slice(1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
||||||
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name], sels);
|
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name], sels);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
function reduce(v, _sels, idxName) {
|
||||||
|
let sels = _sels || [];
|
||||||
|
let sizes = v.sizes || [];
|
||||||
|
|
||||||
|
let accSizes = [1];
|
||||||
|
for (let i=sizes.length-1; i>0; i--) {
|
||||||
|
accSizes = [accSizes[0]*sizes[i], ...accSizes];
|
||||||
|
}
|
||||||
|
const res = Object.assign({}, v);
|
||||||
|
res.sizes = sizes.slice(sels.length);
|
||||||
|
for (let i=0; i<sels.length; i++) {
|
||||||
|
res[idxName] += sels[i]*accSizes[i];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScopeLevel(ctx, name) {
|
function getScopeLevel(ctx, name) {
|
||||||
@@ -255,6 +277,10 @@ function execTemplateDef(ctx, ast) {
|
|||||||
filePath: ctx.filePath,
|
filePath: ctx.filePath,
|
||||||
scopes: copyScope(ctx.scopes)
|
scopes: copyScope(ctx.scopes)
|
||||||
};
|
};
|
||||||
|
ctx.templates[ast.name] = {
|
||||||
|
block: ast.block,
|
||||||
|
params: ast.params
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function execFunctionDef(ctx, ast) {
|
function execFunctionDef(ctx, ast) {
|
||||||
@@ -272,6 +298,10 @@ function execFunctionDef(ctx, ast) {
|
|||||||
filePath: ctx.filePath,
|
filePath: ctx.filePath,
|
||||||
scopes: copyScope(ctx.scopes)
|
scopes: copyScope(ctx.scopes)
|
||||||
};
|
};
|
||||||
|
ctx.functions[ast.name] = {
|
||||||
|
block: ast.block,
|
||||||
|
params: ast.params
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -281,8 +311,6 @@ function execDeclareComponent(ctx, ast) {
|
|||||||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
|
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);
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||||
|
|
||||||
const baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
|
||||||
|
|
||||||
const sizes=[];
|
const sizes=[];
|
||||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||||
const size = exec(ctx, ast.name.selectors[i]);
|
const size = exec(ctx, ast.name.selectors[i]);
|
||||||
@@ -293,16 +321,13 @@ function execDeclareComponent(ctx, ast) {
|
|||||||
sizes.push( size.value.toJSNumber() );
|
sizes.push( size.value.toJSNumber() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cIdx = ctx.addComponent(ast.name.name, sizes);
|
||||||
|
|
||||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
scope[ast.name.name] = {
|
||||||
|
|
||||||
ctx.components[fullName] = "UNINSTANTIATED";
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "COMPONENT",
|
type: "COMPONENT",
|
||||||
fullName: fullName
|
sizes: sizes,
|
||||||
|
cIdx: Array.isArray(cIdx) ? cIdx[0] : cIdx
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "VARIABLE",
|
type: "VARIABLE",
|
||||||
@@ -333,15 +358,20 @@ function execInstantiateComponet(ctx, vr, fn) {
|
|||||||
|
|
||||||
paramValues.push(v);
|
paramValues.push(v);
|
||||||
}
|
}
|
||||||
if (template.params.length != paramValues.length) error(ctx, fn, "Invalid Number of parameters");
|
if (template.params.length != paramValues.length) return error(ctx, fn, "Invalid Number of parameters");
|
||||||
|
|
||||||
const vv = getScope(ctx, componentName, vr.selectors);
|
|
||||||
|
|
||||||
if (!vv) return error(ctx, vr, "Component not defined"+ componentName);
|
const vComp = getScope(ctx, componentName, vr.selectors);
|
||||||
|
if (vComp.type != "COMPONENT") return error(ctx, fn, "Assigning to a non component");
|
||||||
|
const cIdx = vComp.cIdx;
|
||||||
|
if (cIdx == -1) return error(ctx, fn, "Component not defined");
|
||||||
|
let l=1;
|
||||||
|
for (let i=0; i<vComp.sizes.length; i++) l = l*vComp.sizes[i];
|
||||||
|
for (let i=0; i<l; i++) {
|
||||||
|
instantiateComponent(cIdx+i);
|
||||||
|
}
|
||||||
|
|
||||||
instantiateComponent(vv);
|
function instantiateComponent(cIdx) {
|
||||||
|
|
||||||
function instantiateComponent(varVal) {
|
|
||||||
|
|
||||||
function extractValue(v) {
|
function extractValue(v) {
|
||||||
if (Array.isArray(v)) {
|
if (Array.isArray(v)) {
|
||||||
@@ -351,23 +381,25 @@ function execInstantiateComponet(ctx, vr, fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(varVal)) {
|
if (ctx.components[cIdx]) return error(ctx, fn, "Component already instantiated");
|
||||||
for (let i =0; i<varVal.length; i++) {
|
|
||||||
instantiateComponent(varVal[i]);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.components[varVal.fullName] != "UNINSTANTIATED") error(ctx, fn, "Component already instantiated");
|
|
||||||
|
|
||||||
const oldComponent = ctx.currentComponent;
|
const oldComponent = ctx.currentComponent;
|
||||||
const oldFileName = ctx.fileName;
|
const oldFileName = ctx.fileName;
|
||||||
const oldFilePath = ctx.filePath;
|
const oldFilePath = ctx.filePath;
|
||||||
ctx.currentComponent = varVal.fullName;
|
const oldMain = ctx.main;
|
||||||
|
|
||||||
ctx.components[ctx.currentComponent] = {
|
if ((componentName == "main")&&(ctx.currentComponent==-1)) {
|
||||||
signals: [],
|
ctx.main=true;
|
||||||
params: {}
|
} else {
|
||||||
|
ctx.main=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.currentComponent = cIdx;
|
||||||
|
|
||||||
|
ctx.components[cIdx] = {
|
||||||
|
params: {},
|
||||||
|
names: ctx.newTableName(),
|
||||||
|
nInSignals: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const oldScopes = ctx.scopes;
|
const oldScopes = ctx.scopes;
|
||||||
@@ -379,20 +411,22 @@ function execInstantiateComponet(ctx, vr, fn) {
|
|||||||
const scope = {};
|
const scope = {};
|
||||||
for (let i=0; i< template.params.length; i++) {
|
for (let i=0; i< template.params.length; i++) {
|
||||||
scope[template.params[i]] = paramValues[i];
|
scope[template.params[i]] = paramValues[i];
|
||||||
ctx.components[ctx.currentComponent].params[template.params[i]] = extractValue(paramValues[i]);
|
ctx.components[cIdx].params[template.params[i]] = extractValue(paramValues[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.components[ctx.currentComponent].template = templateName;
|
ctx.components[cIdx].template = templateName;
|
||||||
ctx.fileName = template.fileName;
|
ctx.fileName = template.fileName;
|
||||||
ctx.filePath = template.filePath;
|
ctx.filePath = template.filePath;
|
||||||
ctx.scopes = copyScope( template.scopes );
|
ctx.scopes = copyScope( template.scopes );
|
||||||
ctx.scopes.push(scope);
|
ctx.scopes.push(scope);
|
||||||
|
|
||||||
|
|
||||||
execBlock(ctx, template.block);
|
execBlock(ctx, template.block);
|
||||||
|
|
||||||
ctx.fileName = oldFileName;
|
ctx.fileName = oldFileName;
|
||||||
ctx.filePath = oldFilePath;
|
ctx.filePath = oldFilePath;
|
||||||
ctx.currentComponent = oldComponent;
|
ctx.currentComponent = oldComponent;
|
||||||
|
ctx.main = oldMain;
|
||||||
ctx.scopes = oldScopes;
|
ctx.scopes = oldScopes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -461,32 +495,47 @@ function execDeclareSignal(ctx, ast) {
|
|||||||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
|
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);
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||||
|
|
||||||
const baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
|
||||||
|
|
||||||
const sizes=[];
|
const sizes=[];
|
||||||
|
let totalSize=1;
|
||||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||||
const size = exec(ctx, ast.name.selectors[i]);
|
const size = exec(ctx, ast.name.selectors[i]);
|
||||||
if (ctx.error) return;
|
if (ctx.error) return;
|
||||||
|
|
||||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||||
sizes.push( size.value.toJSNumber() );
|
const s = size.value.toJSNumber();
|
||||||
|
totalSize *= s;
|
||||||
|
sizes.push( s );
|
||||||
}
|
}
|
||||||
|
|
||||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
let sIdx = ctx.addSignal(ast.name.name, sizes);
|
||||||
ctx.signals[fullName] = {
|
if (!Array.isArray(sIdx)) sIdx = [sIdx, sIdx+1];
|
||||||
fullName: fullName,
|
for (let i=sIdx[0]; i<sIdx[1]; i++) {
|
||||||
direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""),
|
ctx.signals[i] = {
|
||||||
private: ast.private,
|
o: 0,
|
||||||
component: ctx.currentComponent,
|
e: -1
|
||||||
equivalence: "",
|
|
||||||
alias: [fullName]
|
|
||||||
};
|
};
|
||||||
ctx.components[ctx.currentComponent].signals.push(fullName);
|
|
||||||
return {
|
if (ast.declareType == "SIGNALIN") {
|
||||||
|
ctx.signals[i].o |= ctx.IN;
|
||||||
|
ctx.components[ctx.currentComponent].nInSignals+=1;
|
||||||
|
}
|
||||||
|
if (ast.declareType == "SIGNALOUT") {
|
||||||
|
ctx.signals[i].o |= ctx.OUT;
|
||||||
|
}
|
||||||
|
if (ast.private ) {
|
||||||
|
ctx.signals[i].o |= ctx.PRV;
|
||||||
|
}
|
||||||
|
if (ctx.main) {
|
||||||
|
ctx.signals[i].o |= ctx.MAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctx.components[ctx.currentComponent].signals.push(i);
|
||||||
|
}
|
||||||
|
scope[ast.name.name] = {
|
||||||
type: "SIGNAL",
|
type: "SIGNAL",
|
||||||
fullName: fullName,
|
sizes: sizes,
|
||||||
|
sIdx: sIdx[0]
|
||||||
};
|
};
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
type: "VARIABLE",
|
type: "VARIABLE",
|
||||||
name: ast.name.name,
|
name: ast.name.name,
|
||||||
@@ -535,10 +584,10 @@ function execVariable(ctx, ast) {
|
|||||||
if (!v) return error(ctx, ast, "Variable not defined");
|
if (!v) return error(ctx, ast, "Variable not defined");
|
||||||
|
|
||||||
// If the signal has an assigned value (constant) just return the constant
|
// If the signal has an assigned value (constant) just return the constant
|
||||||
if ((v.type == "SIGNAL") && (ctx.signals[v.fullName].value)) {
|
if ((v.type == "SIGNAL") && (ctx.signals[v.sIdx].value)) {
|
||||||
return {
|
return {
|
||||||
type: "NUMBER",
|
type: "NUMBER",
|
||||||
value: ctx.signals[v.fullName].value
|
value: ctx.signals[v.sIdx].value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let res;
|
let res;
|
||||||
@@ -547,23 +596,43 @@ function execVariable(ctx, ast) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function execPin(ctx, ast) {
|
function execPin(ctx, ast) {
|
||||||
const component = getScope(ctx, ast.component.name, ast.component.selectors);
|
const selsC = [];
|
||||||
if (!component) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
|
for (let i=0; i< ast.component.selectors.length; i++) {
|
||||||
if (ctx.error) return;
|
const sel = exec(ctx, ast.component.selectors[i]);
|
||||||
let signalFullName = component.fullName + "." + ast.pin.name;
|
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||||
|
selsC.push(sel.value.toJSNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
|
||||||
|
if (cIdx<0) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
|
||||||
|
|
||||||
|
const selsP = [];
|
||||||
for (let i=0; i< ast.pin.selectors.length; i++) {
|
for (let i=0; i< ast.pin.selectors.length; i++) {
|
||||||
const sel = exec(ctx, ast.pin.selectors[i]);
|
const sel = exec(ctx, ast.pin.selectors[i]);
|
||||||
if (ctx.error) return;
|
|
||||||
|
|
||||||
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||||
|
selsP.push(sel.value.toJSNumber());
|
||||||
signalFullName += "[" + sel.value.toJSNumber() + "]";
|
|
||||||
}
|
}
|
||||||
if (!ctx.signals[signalFullName]) error(ctx, ast, "Signal not defined:" + signalFullName);
|
const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
|
||||||
|
|
||||||
|
if (sIdx<0) error(ctx, ast, "Signal not defined:" + buildFullName() );
|
||||||
return {
|
return {
|
||||||
type: "SIGNAL",
|
type: "SIGNAL",
|
||||||
fullName: signalFullName
|
sIdx: sIdx
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function buildFullName() {
|
||||||
|
return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sels2str(sels) {
|
||||||
|
let S = "";
|
||||||
|
for (let i=0; i< sels.length; i++) {
|
||||||
|
const sel = exec(ctx, ast.pin.selectors[i]);
|
||||||
|
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||||
|
S += "[" + sel.value.toString() + "]";
|
||||||
|
}
|
||||||
|
return S;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function execFor(ctx, ast) {
|
function execFor(ctx, ast) {
|
||||||
@@ -994,13 +1063,13 @@ function execSignalAssign(ctx, ast) {
|
|||||||
if (!dst) return error(ctx, ast, "Signal not defined");
|
if (!dst) return error(ctx, ast, "Signal not defined");
|
||||||
if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
|
if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
|
||||||
|
|
||||||
let sDest=ctx.signals[dst.fullName];
|
let sDest=ctx.signals[dst.sIdx];
|
||||||
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.fullName);
|
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.sIdx);
|
||||||
|
|
||||||
let isOut = (sDest.component == "main")&&(sDest.direction=="OUT");
|
let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
|
||||||
while (sDest.equivalence) {
|
while (sDest.e>=0) {
|
||||||
sDest=ctx.signals[sDest.equivalence];
|
sDest=ctx.signals[sDest.e];
|
||||||
isOut = isOut || ((sDest.component == "main")&&(sDest.direction=="OUT"));
|
isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
|
if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
|
||||||
@@ -1029,18 +1098,17 @@ function execSignalAssign(ctx, ast) {
|
|||||||
|
|
||||||
let assignValue = true;
|
let assignValue = true;
|
||||||
if (src.type == "SIGNAL") {
|
if (src.type == "SIGNAL") {
|
||||||
let sSrc = ctx.signals[src.fullName];
|
let sSrc = ctx.signals[src.sIdx];
|
||||||
let isIn = (sSrc.component == "main")&&(sSrc.direction == "IN");
|
let isIn = (sSrc.o & ctx.main)&&(sSrc.o & ctx.IN);
|
||||||
while (sSrc.equivalence) {
|
while (sSrc.e>=0) {
|
||||||
sSrc=ctx.signals[sSrc.equivalence];
|
sSrc=ctx.signals[sSrc.e];
|
||||||
isIn = isIn || ((sSrc.component == "main")&&(sSrc.direction == "IN"));
|
isIn = isIn || ((sSrc.o & ctx.main)&&(sSrc.o & ctx.IN));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if an out is assigned directly to an input.
|
// Skip if an out is assigned directly to an input.
|
||||||
if ((!isIn)||(!isOut)) {
|
if ((!isIn)||(!isOut)) {
|
||||||
sDest.equivalence = src.fullName;
|
sDest.e = src.sIdx;
|
||||||
sDest.alias = sDest.alias.concat(src.alias);
|
while (sDest.e >= 0) sDest=ctx.signals[sDest.e];
|
||||||
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
|
||||||
assignValue = false;
|
assignValue = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1095,10 +1163,11 @@ function execInclude(ctx, ast) {
|
|||||||
ctx.includedFiles = ctx.includedFiles || [];
|
ctx.includedFiles = ctx.includedFiles || [];
|
||||||
if (ctx.includedFiles[incFileName]) return;
|
if (ctx.includedFiles[incFileName]) return;
|
||||||
|
|
||||||
ctx.includedFiles[incFileName] = true;
|
|
||||||
|
|
||||||
const src = fs.readFileSync(incFileName, "utf8");
|
const src = fs.readFileSync(incFileName, "utf8");
|
||||||
|
|
||||||
|
ctx.includedFiles[incFileName] = src.split("\n");
|
||||||
|
|
||||||
if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
|
if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
|
||||||
|
|
||||||
const incAst = parser.parse(src);
|
const incAst = parser.parse(src);
|
||||||
|
|||||||
@@ -188,26 +188,28 @@ function genBlock(ctx, ast) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function genTemplateDef(ctx, ast) {
|
function genTemplateDef(ctx, ast) {
|
||||||
let S = "function(ctx) ";
|
|
||||||
|
|
||||||
const newScope = {};
|
if (ctx.f) return error(ctx, ast, "Already in function");
|
||||||
|
|
||||||
|
ctx.f = ctx.module.addFunction(ast.name);
|
||||||
|
ctx.c = ctx.f.getCodeBuilder();
|
||||||
|
|
||||||
|
ctx.scope = {};
|
||||||
for (let i=0; i< ast.params.length; i++) {
|
for (let i=0; i< ast.params.length; i++) {
|
||||||
newScope[ast.params[i]] = { type: "VARIABLE" };
|
ctx.f.addParam(ast.params[i].name, "i32");
|
||||||
|
ctx.scope[ast.params[i].name] = {
|
||||||
|
type: "PARAM",
|
||||||
|
sels: ast.params[i].sels,
|
||||||
|
getter: () => { return ctx.c.getLocal(ast.params[i].name); },
|
||||||
|
setter: (v) => { return ctx.c.setLocal(ast.params[i].name, v); }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.scopes.push(newScope);
|
genBlock(ctx, ast.block);
|
||||||
S += genBlock(ctx, ast.block);
|
|
||||||
ctx.scopes.pop();
|
|
||||||
|
|
||||||
// const scope = ctx.scopes[ctx.scopes.length-1];
|
ctx.scope = null;
|
||||||
const scope = ctx.scopes[0]; // Scope for templates is top
|
ctx.c = null;
|
||||||
|
ctx.f = null;
|
||||||
scope[ast.name] = {
|
|
||||||
type: "TEMPLATE"
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.templates[ast.name] = S;
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function genFunctionDef(ctx, ast) {
|
function genFunctionDef(ctx, ast) {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ QEQ QEQ ERR ERR
|
|||||||
|
|
||||||
const bigInt = require("big-integer");
|
const bigInt = require("big-integer");
|
||||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
|
const sONE = 0;
|
||||||
|
|
||||||
exports.add = add;
|
exports.add = add;
|
||||||
exports.mul = mul;
|
exports.mul = mul;
|
||||||
@@ -79,7 +80,7 @@ function signal2lc(a) {
|
|||||||
type: "LINEARCOMBINATION",
|
type: "LINEARCOMBINATION",
|
||||||
values: {}
|
values: {}
|
||||||
};
|
};
|
||||||
lc.values[a.fullName] = bigInt(1);
|
lc.values[a.sIdx] = bigInt(1);
|
||||||
return lc;
|
return lc;
|
||||||
} else {
|
} else {
|
||||||
return a;
|
return a;
|
||||||
@@ -163,10 +164,10 @@ function addLCNum(a,b) {
|
|||||||
return { type: "ERROR", errStr: "LinearCombination + undefined" };
|
return { type: "ERROR", errStr: "LinearCombination + undefined" };
|
||||||
}
|
}
|
||||||
if (b.value.isZero()) return res;
|
if (b.value.isZero()) return res;
|
||||||
if (!res.values["one"]) {
|
if (!res.values[sONE]) {
|
||||||
res.values["one"]=bigInt(b.value);
|
res.values[sONE]=bigInt(b.value);
|
||||||
} else {
|
} else {
|
||||||
res.values["one"]= res.values["one"].add(b.value).mod(__P__);
|
res.values[sONE]= res.values[sONE].add(b.value).mod(__P__);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -278,16 +279,16 @@ function mulQEQNum(a,b) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSignalValue(ctx, signalName) {
|
function getSignalValue(ctx, sIdx) {
|
||||||
const s = ctx.signals[signalName];
|
const s = ctx.signals[sIdx];
|
||||||
if (s.equivalence != "") {
|
if (s.e >= 0) {
|
||||||
return getSignalValue(ctx, s.equivalence);
|
return getSignalValue(ctx, s.e);
|
||||||
} else {
|
} else {
|
||||||
const res = {
|
const res = {
|
||||||
type: "NUMBER"
|
type: "NUMBER"
|
||||||
};
|
};
|
||||||
if (s.value) {
|
if (s.v) {
|
||||||
res.value = s.value;
|
res.value = s.v;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -297,7 +298,7 @@ function evaluate(ctx, n) {
|
|||||||
if (n.type == "NUMBER") {
|
if (n.type == "NUMBER") {
|
||||||
return n;
|
return n;
|
||||||
} else if (n.type == "SIGNAL") {
|
} else if (n.type == "SIGNAL") {
|
||||||
return getSignalValue(ctx, n.fullName);
|
return getSignalValue(ctx, n.sIdx);
|
||||||
} else if (n.type == "LINEARCOMBINATION") {
|
} else if (n.type == "LINEARCOMBINATION") {
|
||||||
const v= {
|
const v= {
|
||||||
type: "NUMBER",
|
type: "NUMBER",
|
||||||
@@ -362,7 +363,7 @@ function toQEQ(a) {
|
|||||||
type: "QEQ",
|
type: "QEQ",
|
||||||
a: {type: "LINEARCOMBINATION", values: {}},
|
a: {type: "LINEARCOMBINATION", values: {}},
|
||||||
b: {type: "LINEARCOMBINATION", values: {}},
|
b: {type: "LINEARCOMBINATION", values: {}},
|
||||||
c: {type: "LINEARCOMBINATION", values: {"one": bigInt(a.value)}}
|
c: {type: "LINEARCOMBINATION", values: {sONE: bigInt(a.value)}}
|
||||||
};
|
};
|
||||||
} else if (a.type == "LINEARCOMBINATION") {
|
} else if (a.type == "LINEARCOMBINATION") {
|
||||||
return {
|
return {
|
||||||
@@ -415,11 +416,11 @@ function toString(a, ctx) {
|
|||||||
if (!c.equals(1)) {
|
if (!c.equals(1)) {
|
||||||
S = S + c.toString() + "*";
|
S = S + c.toString() + "*";
|
||||||
}
|
}
|
||||||
let sigName = k;
|
let sIdx = k;
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
while (ctx.signals[sigName].equivalence) sigName = ctx.signals[sigName].equivalence;
|
while (ctx.signals[sIdx].e>=0) sIdx = ctx.signals[sIdx].e;
|
||||||
}
|
}
|
||||||
S = S + sigName;
|
S = S + "[" + sIdx + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (S=="") return "0"; else return S;
|
if (S=="") return "0"; else return S;
|
||||||
@@ -437,13 +438,13 @@ function canonize(ctx, a) {
|
|||||||
const res = clone(a);
|
const res = clone(a);
|
||||||
for (let k in a.values) {
|
for (let k in a.values) {
|
||||||
let s = k;
|
let s = k;
|
||||||
while (ctx.signals[s].equivalence) s= ctx.signals[s].equivalence;
|
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
|
||||||
if ((typeof(ctx.signals[s].value) != "undefined")&&(k != "one")) {
|
if ((typeof(ctx.signals[s].value) != "undefined")&&(k != sONE)) {
|
||||||
const v = res.values[k].times(ctx.signals[s].value).mod(__P__);
|
const v = res.values[k].times(ctx.signals[s].value).mod(__P__);
|
||||||
if (!res.values["one"]) {
|
if (!res.values[sONE]) {
|
||||||
res.values["one"]=v;
|
res.values[sONE]=v;
|
||||||
} else {
|
} else {
|
||||||
res.values["one"]= res.values["one"].add(v).mod(__P__);
|
res.values[sONE]= res.values[sONE].add(v).mod(__P__);
|
||||||
}
|
}
|
||||||
delete res.values[k];
|
delete res.values[k];
|
||||||
} else if (s != k) {
|
} else if (s != k) {
|
||||||
|
|||||||
55
src/utils.js
Normal file
55
src/utils.js
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1
test/circuits/in.bin
Normal file
1
test/circuits/in.bin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
1
test/circuits/in.json
Normal file
1
test/circuits/in.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "in1": 1, "in2": [2,3], "in3":[[4,5],[6,7],[8,9]] }
|
||||||
@@ -1,18 +1,54 @@
|
|||||||
template Internal() {
|
template Internal() {
|
||||||
signal input in;
|
signal input in1;
|
||||||
signal output out;
|
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() {
|
template InOut() {
|
||||||
signal input in;
|
signal input in1;
|
||||||
signal output out;
|
signal input in2[2];
|
||||||
|
signal input in3[3][2];
|
||||||
|
|
||||||
|
signal output out1;
|
||||||
|
signal output out2[2];
|
||||||
|
signal output out3[3][2];
|
||||||
|
|
||||||
component internal = Internal();
|
component internal = Internal();
|
||||||
|
|
||||||
internal.in <== in;
|
internal.in1 <== in1;
|
||||||
internal.out ==> out;
|
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();
|
component main = InOut();
|
||||||
|
|||||||
20
test/inout.js
Normal file
20
test/inout.js
Normal file
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user