@ -0,0 +1,155 @@ |
|||||
|
#include <string>
|
||||
|
#include <stdexcept>
|
||||
|
#include <sstream>
|
||||
|
#include <iostream>
|
||||
|
#include <iomanip>
|
||||
|
#include <stdlib.h>
|
||||
|
#include <gmp.h>
|
||||
|
#include "calcwit.h"
|
||||
|
#include "utils.h"
|
||||
|
|
||||
|
Circom_CalcWit::Circom_CalcWit(Circom_Circuit *aCircuit) { |
||||
|
circuit = aCircuit; |
||||
|
|
||||
|
#ifdef SANITY_CHECK
|
||||
|
signalAssigned = new bool[circuit->NSignals]; |
||||
|
signalAssigned[0] = true; |
||||
|
#endif
|
||||
|
|
||||
|
inputSignalsToTrigger = new int[circuit->NComponents]; |
||||
|
signalValues = new BigInt[circuit->NSignals]; |
||||
|
|
||||
|
// Set one signal
|
||||
|
mpz_init_set_ui(signalValues[0], 1); |
||||
|
|
||||
|
// Initialize remaining signals
|
||||
|
for (int i=1; i<circuit->NSignals; i++) mpz_init2(signalValues[i], 256); |
||||
|
|
||||
|
reset(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void Circom_CalcWit::reset() { |
||||
|
|
||||
|
#ifdef SANITY_CHECK
|
||||
|
for (int i=1; i<circuit->NComponents; i++) signalAssigned[i] = false; |
||||
|
#endif
|
||||
|
|
||||
|
for (int i=0; i<circuit->NComponents; i++) { |
||||
|
inputSignalsToTrigger[i] = circuit->components[i].inputSignals; |
||||
|
if (inputSignalsToTrigger[i] == 0) triggerComponent(i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Circom_CalcWit::~Circom_CalcWit() { |
||||
|
#ifdef SANITY_CHECK
|
||||
|
delete signalAssigned; |
||||
|
#endif
|
||||
|
|
||||
|
for (int i=0; i<circuit->NSignals; i++) mpz_clear(signalValues[i]); |
||||
|
|
||||
|
delete[] signalValues; |
||||
|
delete inputSignalsToTrigger; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
int Circom_CalcWit::getSubComponentOffset(int cIdx, u64 hash) { |
||||
|
int hIdx; |
||||
|
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) { |
||||
|
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash)); |
||||
|
} |
||||
|
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos; |
||||
|
if (circuit->components[cIdx].entries[entryPos].type != _typeComponent) { |
||||
|
throw std::runtime_error("invalid type"); |
||||
|
} |
||||
|
return circuit->components[cIdx].entries[entryPos].offset; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Circom_Sizes Circom_CalcWit::getSubComponentSizes(int cIdx, u64 hash) { |
||||
|
int hIdx; |
||||
|
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) { |
||||
|
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash)); |
||||
|
} |
||||
|
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos; |
||||
|
if (circuit->components[cIdx].entries[entryPos].type != _typeComponent) { |
||||
|
throw std::runtime_error("invalid type"); |
||||
|
} |
||||
|
return circuit->components[cIdx].entries[entryPos].sizes; |
||||
|
} |
||||
|
|
||||
|
int Circom_CalcWit::getSignalOffset(int cIdx, u64 hash) { |
||||
|
int hIdx; |
||||
|
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) { |
||||
|
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash)); |
||||
|
} |
||||
|
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos; |
||||
|
if (circuit->components[cIdx].entries[entryPos].type != _typeSignal) { |
||||
|
throw std::runtime_error("invalid type"); |
||||
|
} |
||||
|
return circuit->components[cIdx].entries[entryPos].offset; |
||||
|
} |
||||
|
|
||||
|
Circom_Sizes Circom_CalcWit::getSignalSizes(int cIdx, u64 hash) { |
||||
|
int hIdx; |
||||
|
for(hIdx = int(hash & 0xFF); hash!=circuit->components[cIdx].hashTable[hIdx].hash; hIdx++) { |
||||
|
if (!circuit->components[cIdx].hashTable[hIdx].hash) throw std::runtime_error("hash not found: " + int_to_hex(hash)); |
||||
|
} |
||||
|
int entryPos = circuit->components[cIdx].hashTable[hIdx].pos; |
||||
|
if (circuit->components[cIdx].entries[entryPos].type != _typeSignal) { |
||||
|
throw std::runtime_error("invalid type"); |
||||
|
} |
||||
|
return circuit->components[cIdx].entries[entryPos].sizes; |
||||
|
} |
||||
|
|
||||
|
PBigInt Circom_CalcWit::allocBigInts(Circom_Sizes sizes) { |
||||
|
PBigInt res = new BigInt[sizes[0]]; |
||||
|
for (int i=0; i<sizes[0]; i++) mpz_init2(res[i], 256); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
void Circom_CalcWit::freeBigInts(PBigInt bi, Circom_Sizes sizes) { |
||||
|
for (int i=0; i<sizes[0]; i++) mpz_clear(bi[i]); |
||||
|
delete[] bi; |
||||
|
} |
||||
|
|
||||
|
void Circom_CalcWit::getSignal(int cIdx, int sIdx, PBigInt value) { |
||||
|
mpz_set(*value, signalValues[sIdx]); |
||||
|
} |
||||
|
|
||||
|
void Circom_CalcWit::setSignal(int cIdx, int sIdx, PBigInt value) { |
||||
|
#ifdef SANITY_CHECK
|
||||
|
assert(signalAssigned[sIdx] == false); |
||||
|
signalAssigned[sIdx] = true; |
||||
|
#endif
|
||||
|
mpz_set(signalValues[sIdx], *value); |
||||
|
if ( BITMAP_ISSET(circuit->mapIsInput, sIdx) ) { |
||||
|
inputSignalsToTrigger[cIdx]--; |
||||
|
if (inputSignalsToTrigger[cIdx] == 0) triggerComponent(cIdx); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void Circom_CalcWit::checkConstraint(PBigInt value1, PBigInt value2, char const *err) { |
||||
|
#ifdef SANITY_CHECK
|
||||
|
if (mpz_cmp(*value1, *value2) != 0) { |
||||
|
char *pcV1 = mpz_get_str(0, 10, *value1); |
||||
|
char *pcV2 = mpz_get_str(0, 10, *value1); |
||||
|
std::string sV1 = std::string(pcV1); |
||||
|
std::string sV2 = std::string(pcV2); |
||||
|
free(pcV1); |
||||
|
free(pcV2); |
||||
|
throw std::runtime_error(std::string("Constraint does not match,") + err + ". " + sV1 + " != " + sV2 ); |
||||
|
} |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Circom_CalcWit::triggerComponent(int newCIdx) { |
||||
|
int oldCIdx = cIdx; |
||||
|
cIdx = newCIdx; |
||||
|
(*(circuit->components[newCIdx].fn))(this); |
||||
|
cIdx = oldCIdx; |
||||
|
} |
||||
|
|
@ -0,0 +1,61 @@ |
|||||
|
#ifndef CIRCOM_CALCWIT_H |
||||
|
#define CIRCOM_CALCWIT_H |
||||
|
|
||||
|
#include "circom.h" |
||||
|
|
||||
|
class Circom_CalcWit { |
||||
|
|
||||
|
#ifdef SANITY_CHECK |
||||
|
bool *signalAssigned; |
||||
|
#endif |
||||
|
|
||||
|
// componentStatus -> For each component |
||||
|
// >0 Signals required to trigger |
||||
|
// == 0 Component triggered |
||||
|
int *inputSignalsToTrigger; |
||||
|
|
||||
|
BigInt *signalValues; |
||||
|
|
||||
|
Circom_Circuit *circuit; |
||||
|
|
||||
|
|
||||
|
|
||||
|
void triggerComponent(int newCIdx); |
||||
|
void calculateWitness(void *input, void *output); |
||||
|
|
||||
|
|
||||
|
public: |
||||
|
int cIdx; |
||||
|
// Functions called by the circuit |
||||
|
Circom_CalcWit(Circom_Circuit *aCircuit); |
||||
|
~Circom_CalcWit(); |
||||
|
|
||||
|
int getSubComponentOffset(int cIdx, u64 hash); |
||||
|
Circom_Sizes getSubComponentSizes(int cIdx, u64 hash); |
||||
|
int getSignalOffset(int cIdx, u64 hash); |
||||
|
Circom_Sizes getSignalSizes(int cIdx, u64 hash); |
||||
|
|
||||
|
PBigInt allocBigInts(Circom_Sizes sizes); |
||||
|
void freeBigInts(PBigInt bi, Circom_Sizes sizes); |
||||
|
|
||||
|
void getSignal(int cIdx, int sIdx, PBigInt value); |
||||
|
void setSignal(int cIdx, int sIdx, PBigInt value); |
||||
|
|
||||
|
void checkConstraint(PBigInt value1, PBigInt value2, char const *err); |
||||
|
|
||||
|
|
||||
|
// Public functions |
||||
|
inline void setInput(int idx, PBigInt val) { |
||||
|
setSignal(0, circuit->wit2sig[idx], val); |
||||
|
} |
||||
|
inline void getWitness(int idx, PBigInt val) { |
||||
|
mpz_set(*val, signalValues[circuit->wit2sig[idx]]); |
||||
|
} |
||||
|
|
||||
|
void reset(); |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
|
||||
|
#endif // CIRCOM_CALCWIT_H |
@ -0,0 +1,56 @@ |
|||||
|
#ifndef __CIRCOM_H |
||||
|
#define __CIRCOM_H |
||||
|
|
||||
|
#include <gmp.h> |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
class Circom_CalcWit; |
||||
|
typedef unsigned long long u64; |
||||
|
typedef uint32_t u32; |
||||
|
typedef uint8_t u8; |
||||
|
typedef mpz_t BigInt; |
||||
|
typedef BigInt *PBigInt; |
||||
|
|
||||
|
typedef int Circom_Size; |
||||
|
typedef Circom_Size *Circom_Sizes; |
||||
|
|
||||
|
struct Circom_HashEntry { |
||||
|
u64 hash; |
||||
|
int pos; |
||||
|
}; |
||||
|
typedef Circom_HashEntry *Circom_HashTable; |
||||
|
|
||||
|
typedef enum { _typeSignal, _typeComponent} Circom_EntryType; |
||||
|
|
||||
|
struct Circom_ComponentEntry { |
||||
|
int offset; |
||||
|
Circom_Sizes sizes; |
||||
|
Circom_EntryType type; |
||||
|
}; |
||||
|
typedef Circom_ComponentEntry *Circom_ComponentEntries; |
||||
|
|
||||
|
typedef void (*Circom_ComponentFunction)(Circom_CalcWit *ctx); |
||||
|
|
||||
|
struct Circom_Component { |
||||
|
Circom_HashTable hashTable; |
||||
|
Circom_ComponentEntries entries; |
||||
|
Circom_ComponentFunction fn; |
||||
|
int inputSignals; |
||||
|
}; |
||||
|
|
||||
|
class Circom_Circuit { |
||||
|
public: |
||||
|
int NSignals; |
||||
|
int NComponents; |
||||
|
int NInputs; |
||||
|
int NOutputs; |
||||
|
int NVars; |
||||
|
int *wit2sig; |
||||
|
Circom_Component *components; |
||||
|
u32 *mapIsInput; |
||||
|
}; |
||||
|
|
||||
|
#define BITMAP_ISSET(m, b) (m[b>>5] & (1 << (b&0x1F))) |
||||
|
extern struct Circom_Circuit _circuit; |
||||
|
|
||||
|
#endif |
@ -0,0 +1,196 @@ |
|||||
|
#include <iostream>
|
||||
|
#include <fstream>
|
||||
|
#include <sstream>
|
||||
|
#include <string>
|
||||
|
#include <sys/types.h>
|
||||
|
#include <sys/stat.h>
|
||||
|
#include <sys/mman.h>
|
||||
|
#include <fcntl.h>
|
||||
|
#include <gmp.h>
|
||||
|
#include <unistd.h>
|
||||
|
#include <nlohmann/json.hpp>
|
||||
|
using json = nlohmann::json; |
||||
|
|
||||
|
#include "calcwit.h"
|
||||
|
#include "circom.h"
|
||||
|
#include "utils.h"
|
||||
|
|
||||
|
#define handle_error(msg) \
|
||||
|
do { perror(msg); exit(EXIT_FAILURE); } while (0) |
||||
|
|
||||
|
void loadBin(Circom_CalcWit *ctx, std::string filename) { |
||||
|
int fd; |
||||
|
struct stat sb; |
||||
|
|
||||
|
// map input
|
||||
|
fd = open(filename.c_str(), O_RDONLY); |
||||
|
if (fd == -1) |
||||
|
handle_error("open"); |
||||
|
|
||||
|
if (fstat(fd, &sb) == -1) /* To obtain file size */ |
||||
|
handle_error("fstat"); |
||||
|
|
||||
|
|
||||
|
u8 *in; |
||||
|
|
||||
|
in = (u8 *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
||||
|
if (in == MAP_FAILED) |
||||
|
handle_error("mmap"); |
||||
|
|
||||
|
close(fd); |
||||
|
|
||||
|
BigInt v; |
||||
|
mpz_init2(v, 256); |
||||
|
u8 *p = in; |
||||
|
for (int i=0; i<_circuit.NInputs; i++) { |
||||
|
int len = *(u8 *)p; |
||||
|
p++; |
||||
|
mpz_import(v,len , -1 , 1, 0, 0, p); |
||||
|
p+=len; |
||||
|
ctx->setSignal(0, _circuit.wit2sig[1 + _circuit.NOutputs + i], &v); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
typedef void (*ItFunc)(Circom_CalcWit *ctx, int idx, json val); |
||||
|
|
||||
|
void iterateArr(Circom_CalcWit *ctx, int o, Circom_Sizes sizes, json jarr, ItFunc f) { |
||||
|
if (!jarr.is_array()) { |
||||
|
assert((sizes[0] == 1)&&(sizes[1] == 0)); |
||||
|
f(ctx, o, jarr); |
||||
|
} else { |
||||
|
int n = sizes[0] / sizes[1]; |
||||
|
for (int i=0; i<n; i++) { |
||||
|
iterateArr(ctx, o + i*sizes[1], sizes+1, jarr[i], f); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void itFunc(Circom_CalcWit *ctx, int o, json val) { |
||||
|
|
||||
|
BigInt v; |
||||
|
mpz_init2(v, 256); |
||||
|
|
||||
|
std::string s; |
||||
|
|
||||
|
if (val.is_string()) { |
||||
|
s = val.get<std::string>(); |
||||
|
} else if (val.is_number()) { |
||||
|
|
||||
|
double vd = val.get<double>(); |
||||
|
std::stringstream stream; |
||||
|
stream << std::fixed << std::setprecision(0) << vd; |
||||
|
s = stream.str(); |
||||
|
} else { |
||||
|
handle_error("Invalid JSON type"); |
||||
|
} |
||||
|
|
||||
|
mpz_set_str (v, s.c_str(), 10); |
||||
|
|
||||
|
ctx->setSignal(0, o, &v); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void loadJson(Circom_CalcWit *ctx, std::string filename) { |
||||
|
std::ifstream inStream(filename); |
||||
|
json j; |
||||
|
inStream >> j; |
||||
|
|
||||
|
for (json::iterator it = j.begin(); it != j.end(); ++it) { |
||||
|
// std::cout << it.key() << " => " << it.value() << '\n';
|
||||
|
u64 h = fnv1a(it.key()); |
||||
|
int o = ctx->getSignalOffset(0, h); |
||||
|
Circom_Sizes sizes = ctx->getSignalSizes(0, h); |
||||
|
iterateArr(ctx, o, sizes, it.value(), itFunc); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
void writeOutBin(Circom_CalcWit *ctx, std::string filename) { |
||||
|
FILE *write_ptr; |
||||
|
|
||||
|
write_ptr = fopen(filename.c_str(),"wb"); |
||||
|
|
||||
|
BigInt v; |
||||
|
mpz_init2(v, 256); |
||||
|
|
||||
|
u8 buffOut[256]; |
||||
|
for (int i=0;i<_circuit.NVars;i++) { |
||||
|
size_t size=256; |
||||
|
ctx->getWitness(i, &v); |
||||
|
mpz_export(buffOut+1, &size, -1, 1, -1, 0, v); |
||||
|
*buffOut = (u8)size; |
||||
|
fwrite(buffOut, size+1, 1, write_ptr); |
||||
|
} |
||||
|
fclose(write_ptr); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
void writeOutJson(Circom_CalcWit *ctx, std::string filename) { |
||||
|
|
||||
|
std::ofstream outFile; |
||||
|
outFile.open (filename); |
||||
|
|
||||
|
outFile << "[\n"; |
||||
|
|
||||
|
BigInt v; |
||||
|
mpz_init2(v, 256); |
||||
|
|
||||
|
char pcV[256]; |
||||
|
|
||||
|
for (int i=0;i<_circuit.NVars;i++) { |
||||
|
ctx->getWitness(i, &v); |
||||
|
mpz_get_str(pcV, 10, v); |
||||
|
std::string sV = std::string(pcV); |
||||
|
outFile << (i ? "," : " ") << "\"" << sV << "\"\n"; |
||||
|
} |
||||
|
|
||||
|
outFile << "]\n"; |
||||
|
outFile.close(); |
||||
|
} |
||||
|
|
||||
|
bool hasEnding (std::string const &fullString, std::string const &ending) { |
||||
|
if (fullString.length() >= ending.length()) { |
||||
|
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char *argv[]) { |
||||
|
if (argc!=3) { |
||||
|
std::string cl = argv[0]; |
||||
|
std::string base_filename = cl.substr(cl.find_last_of("/\\") + 1); |
||||
|
std::cout << "Usage: " << base_filename << " <input.<bin|json>> <output.<bin|json>>\n"; |
||||
|
} else { |
||||
|
|
||||
|
// open output
|
||||
|
Circom_CalcWit *ctx = new Circom_CalcWit(&_circuit); |
||||
|
|
||||
|
std::string infilename = argv[1]; |
||||
|
|
||||
|
if (hasEnding(infilename, std::string(".bin"))) { |
||||
|
loadBin(ctx, infilename); |
||||
|
} else if (hasEnding(infilename, std::string(".json"))) { |
||||
|
loadJson(ctx, infilename); |
||||
|
} else { |
||||
|
handle_error("Invalid input extension (.bin / .json)"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
std::string outfilename = argv[2]; |
||||
|
|
||||
|
if (hasEnding(outfilename, std::string(".bin"))) { |
||||
|
writeOutBin(ctx, outfilename); |
||||
|
} else if (hasEnding(outfilename, std::string(".json"))) { |
||||
|
writeOutJson(ctx, outfilename); |
||||
|
} else { |
||||
|
handle_error("Invalid output extension (.bin / .json)"); |
||||
|
} |
||||
|
|
||||
|
delete ctx; |
||||
|
exit(EXIT_SUCCESS); |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
#include <iostream>
|
||||
|
#include <nlohmann/json.hpp>
|
||||
|
using json = nlohmann::json; |
||||
|
|
||||
|
|
||||
|
#include "utils.h"
|
||||
|
#include "circom.h"
|
||||
|
#include "calcwit.h"
|
||||
|
|
||||
|
auto j = R"( |
||||
|
{ |
||||
|
"in": "314" |
||||
|
} |
||||
|
)"_json; |
||||
|
|
||||
|
typedef void (*ItFunc)(int idx, json val); |
||||
|
|
||||
|
void iterateArr(int o, Circom_Sizes sizes, json jarr, ItFunc f) { |
||||
|
if (!jarr.is_array()) { |
||||
|
assert((sizes[0] == 1)&&(sizes[1] == 0)); |
||||
|
f(o, jarr); |
||||
|
} else { |
||||
|
int n = sizes[0] / sizes[1]; |
||||
|
for (int i=0; i<n; i++) { |
||||
|
iterateArr(o + i*sizes[1], sizes+1, jarr[i], f); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void itFunc(int o, json v) { |
||||
|
std::cout << o << " <-- " << v << '\n'; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int main(int argc, char **argv) { |
||||
|
|
||||
|
Circom_CalcWit *ctx = new Circom_CalcWit(&_circuit); |
||||
|
|
||||
|
for (json::iterator it = j.begin(); it != j.end(); ++it) { |
||||
|
// std::cout << it.key() << " => " << it.value() << '\n';
|
||||
|
u64 h = fnv1a(it.key()); |
||||
|
int o = ctx->getSignalOffset(0, h); |
||||
|
Circom_Sizes sizes = ctx->getSignalSizes(0, h); |
||||
|
iterateArr(o, sizes, it.value(), itFunc); |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,25 @@ |
|||||
|
#include <string>
|
||||
|
#include <sstream>
|
||||
|
#include <iostream>
|
||||
|
#include <iomanip>
|
||||
|
#include <stdlib.h>
|
||||
|
|
||||
|
#include "utils.h"
|
||||
|
|
||||
|
std::string int_to_hex( u64 i ) |
||||
|
{ |
||||
|
std::stringstream stream; |
||||
|
stream << "0x" |
||||
|
<< std::setfill ('0') << std::setw(16) |
||||
|
<< std::hex << i; |
||||
|
return stream.str(); |
||||
|
} |
||||
|
|
||||
|
u64 fnv1a(std::string s) { |
||||
|
u64 hash = 0xCBF29CE484222325LL; |
||||
|
for(char& c : s) { |
||||
|
hash ^= u64(c); |
||||
|
hash *= 0x100000001B3LL; |
||||
|
} |
||||
|
return hash; |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
#ifndef __UTILS_H |
||||
|
#define __UTILS_H |
||||
|
|
||||
|
#include "circom.h" |
||||
|
|
||||
|
std::string int_to_hex( u64 i ); |
||||
|
u64 fnv1a(std::string s); |
||||
|
|
||||
|
|
||||
|
#endif // __UTILS_H |
@ -0,0 +1,489 @@ |
|||||
|
# Binary format for R1CS |
||||
|
|
||||
|
--- |
||||
|
eip: |
||||
|
title: r1cs binary format |
||||
|
author: Jordi Baylina <jordi@baylina.cat> |
||||
|
discussions-to: |
||||
|
status: draft |
||||
|
type: Standards Track |
||||
|
category: ERC |
||||
|
created: 2019-09-24 |
||||
|
requires: |
||||
|
--- |
||||
|
|
||||
|
## Simple Summary |
||||
|
|
||||
|
This standard defines a standard format for a binery representation of a r1cs constraint system. |
||||
|
|
||||
|
## Abstract |
||||
|
|
||||
|
|
||||
|
## Motivation |
||||
|
|
||||
|
The zero knowledge primitives, requires the definition of a statment that wants to be proved. This statment can be expressed as a deterministric program or an algebraic circuit. Lots of primitives like zkSnarks, bulletProofs or aurora, requires to convert this statment to a rank-one constraint system. |
||||
|
|
||||
|
This standard specifies a format for a r1cs and allows the to connect a set of tools that compiles a program or a circuit to r1cs that can be used for the zksnarks or bulletproofs primitives. |
||||
|
|
||||
|
## Specification |
||||
|
|
||||
|
### General considerations |
||||
|
|
||||
|
All integers are represented in Little Endian Fix size format |
||||
|
|
||||
|
The standard extension is `.r1cs` |
||||
|
|
||||
|
The constraint is in the form |
||||
|
|
||||
|
$$ |
||||
|
\left\{ \begin{array}{rclclcl} |
||||
|
(a_{0,0}s_0 + a_{0,1}s_1 + ... + a_{0,n-1}s_{n-1}) &\cdot& (b_{0,0} s_0 + b_{0,1} s_1 + ... + b_{0,n-1} s_{n-1}) &-& (c_{0,0} s_0 + c_{0,1} s_1 + ... + c_{0,n-1}s_{n-1}) &=& 0 \\ |
||||
|
(a_{1,0}s_0 + a_{1,1}s_1 + ... + a_{1,n-1}s_{n-1}) &\cdot& (b_{1,0} s_0 + b_{1,1} s_1 + ... + b_{1,n-1} s_{n-1}) &-& (c_{1,0} s_0 + c_{1,1}s_1 + ... + c_{1,n-1}s_{n-1}) &=& 0 \\ |
||||
|
...\\ |
||||
|
(a_{m-1,0}s_0 + a_{m-1,1}s_1 + ... + a_{m-1,n-1}s_{n-1}) &\cdot& (b_{m-1,0} s_0 + b_{m-1,1} s_1 + ... + b_{m-1,n-1} s_{n-1}) &-& (c_{m-1,0} s_0 + c_{m-1,1}s_1 + ... + c_{m-1,n-1}s_{n-1}) &=& 0 |
||||
|
\end{array} \right. |
||||
|
$$ |
||||
|
|
||||
|
|
||||
|
### Format |
||||
|
|
||||
|
```` |
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ 4 │ 72 31 63 73 ┃ Magic "r1cs" |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ 4 │ 01 00 00 00 ┃ Version 1 |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ 4 │ nW ┃ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ nW │ 01 00 00 00 ┃ nWires |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ nW │ 01 00 00 00 ┃ nPubOut |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ nW │ 01 00 00 00 ┃ nPubIn |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ nW │ 01 00 00 00 ┃ nPrvIn |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ nW │m := NConstraints┃ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲ |
||||
|
┃ nW │ nA ┃ ╲ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲ |
||||
|
┃ nW │ idx_1 ┃ V │ a_{0,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
||||
|
┃ nW │ idx_2 ┃ V │ a_{0,idx_2} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
... ... │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nA ┃ V │ a_{0,idx_nA} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ nB ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_1 ┃ V │ b_{0,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲ |
||||
|
┃ nW │ idx_2 ┃ V │ b_{0,idx_2} ┃ ╲ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_0 |
||||
|
... ... ╱ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nB ┃ V │ b_{0,idx_nB} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ nC ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_1 ┃ V │ c_{0,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
||||
|
┃ nW │ idx_2 ┃ V │ c_{0,idx_2} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
... ... │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nB ┃ V │ c_{0,idx_nC} ┃ ╱ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ |
||||
|
╱ |
||||
|
|
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲ |
||||
|
┃ nW │ nA ┃ ╲ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲ |
||||
|
┃ nW │ idx_1 ┃ V │ a_{1,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
||||
|
┃ nW │ idx_2 ┃ V │ a_{1,idx_2} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
... ... │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nA ┃ V │ a_{1,idx_nA} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ nB ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_1 ┃ V │ b_{1,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲ |
||||
|
┃ nW │ idx_2 ┃ V │ b_{1,idx_2} ┃ ╲ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_1 |
||||
|
... ... ╱ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nB ┃ V │ b_{1,idx_nB} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ nC ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_1 ┃ V │ c_{1,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
||||
|
┃ nW │ idx_2 ┃ V │ c_{1,idx_2} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
... ... │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nB ┃ V │ c_{1,idx_nC} ┃ ╱ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ |
||||
|
╱ |
||||
|
|
||||
|
... |
||||
|
... |
||||
|
... |
||||
|
|
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲ |
||||
|
┃ nW │ nA ┃ ╲ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲ |
||||
|
┃ nW │ idx_1 ┃ V │ a_{m-1,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
||||
|
┃ nW │ idx_2 ┃ V │ a_{m-1,idx_2} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
... ... │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nA ┃ V │ a_{m-1,idx_nA} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ nB ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_1 ┃ V │ b_{m-1,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲ |
||||
|
┃ nW │ idx_2 ┃ V │ b_{m-1,idx_2} ┃ ╲ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_{m-1} |
||||
|
... ... ╱ |
||||
|
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW idx_nB ┃ V │ b_{m-1,idx_nB} ┃ │ |
||||
|
┗━━━━━━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ nC ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_1 ┃ V │ c_{m-1,idx_1} ┃ │ |
||||
|
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │ |
||||
|
┃ nW │ idx_2 ┃ V │ c_{m-1,idx_2} ┃ │ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │ |
||||
|
... ... │ |
||||
|
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │ |
||||
|
┃ nW │ idx_nB ┃ V │ c_{m-1,idx_nC} ┃ ╱ |
||||
|
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ |
||||
|
╱ ╱ |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
### Magic Number |
||||
|
|
||||
|
Size: 4 bytes |
||||
|
The file start with a constant 4 byts (magic number) "r1cs" |
||||
|
|
||||
|
``` |
||||
|
0x72 0x31 0x63 0x73 |
||||
|
``` |
||||
|
|
||||
|
### Version |
||||
|
|
||||
|
Size: 4 bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
For this standard it's fixed to |
||||
|
|
||||
|
``` |
||||
|
0x01 0x00 0x00 0x00 |
||||
|
``` |
||||
|
|
||||
|
### Word With (nW) |
||||
|
|
||||
|
Size: 4 bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
This is the standard word size in bytes used to specify lenghts and indexes in the file. |
||||
|
|
||||
|
The format of this field is little endian. |
||||
|
|
||||
|
In most of the cases this will be 4 (32bit values) |
||||
|
|
||||
|
Example: |
||||
|
``` |
||||
|
0x04 0x00 0x00 0x00 |
||||
|
``` |
||||
|
|
||||
|
### Number of wires |
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Total Number of wires including ONE signal (Index 0). |
||||
|
|
||||
|
### Number of public outputs |
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Total Number of wires public output wires. They should be starting at idx 1 |
||||
|
|
||||
|
### Number of public inputs |
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Total Number of wires public input wires. They should be starting just after the public output |
||||
|
|
||||
|
### Number of private inputs |
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Total Number of wires private input wires. They should be starting just after the public inputs |
||||
|
|
||||
|
### Number of constraints |
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Total Number of constraints |
||||
|
|
||||
|
### Constraints |
||||
|
|
||||
|
Each constraint contains 3 linear combinations A, B, C. |
||||
|
|
||||
|
The constraint is such that: |
||||
|
``` |
||||
|
A*B-C = 0 |
||||
|
``` |
||||
|
|
||||
|
### Linear combination |
||||
|
|
||||
|
Each linear combination is of the form: |
||||
|
|
||||
|
$$ |
||||
|
a_{0,0}s_0 + a_{0,1}s_1 + ... + a_{0,n-1}s_{n-1} |
||||
|
$$ |
||||
|
|
||||
|
### Number of nonZero Factors |
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Total number of non Zero factors in the linear compination. |
||||
|
|
||||
|
The factors MUST be sorted in ascending order. |
||||
|
|
||||
|
### Factor |
||||
|
|
||||
|
For each factor we have the index of the factor and the value of the factor. |
||||
|
|
||||
|
### Index of the factor |
||||
|
|
||||
|
|
||||
|
Size: nW bytes |
||||
|
Format: Little-Endian |
||||
|
|
||||
|
Index of the nonZero Factor |
||||
|
|
||||
|
### Value of the factor |
||||
|
|
||||
|
The first byte indicate the length N in bytes of the number in the upcoming bytes. |
||||
|
|
||||
|
The next N bytes represent the value in Little Endian format. |
||||
|
|
||||
|
For example, to represent the linear combination: |
||||
|
|
||||
|
$$ |
||||
|
5s_4 +8s_5 + 260s_886 |
||||
|
$$ |
||||
|
|
||||
|
The linear combination would be represented as: |
||||
|
|
||||
|
```` |
||||
|
┏━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ 03 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┓ |
||||
|
┃ 04 00 00 00 ┃ 01 05 ┃ |
||||
|
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┫ |
||||
|
┃ 05 00 00 00 ┃ 01 08 ┃ |
||||
|
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┫ |
||||
|
┃ 76 03 00 00 ┃ 02 04 01 ┃ |
||||
|
┗━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┛ |
||||
|
```` |
||||
|
## Rationale |
||||
|
|
||||
|
Variable size for field elements allows to shrink the size of the file and allows to work with any field. |
||||
|
|
||||
|
Version allows to update the format. |
||||
|
|
||||
|
Have a very good comprasion ratio for sparse r1cs as it's the normal case. |
||||
|
|
||||
|
|
||||
|
## Backward Compatibility |
||||
|
|
||||
|
N.A. |
||||
|
|
||||
|
## Test Cases |
||||
|
### Example |
||||
|
|
||||
|
Given this r1cs in a 256 bit Field: |
||||
|
|
||||
|
|
||||
|
$$ |
||||
|
\left\{ \begin{array}{rclclcl} |
||||
|
(3s_5 + 8s_6) &\cdot& (2s_0 + 20s_2 + 12s_3) &-& (5s_0 + 7s_9) &=& 0 \\ |
||||
|
(4s_1 + 8s_5 + 3s_9) &\cdot& (6s_6 + 44s_3) && &=& 0 \\ |
||||
|
(4s_6) &\cdot& (6s_0 + 5s_3 + 11s_9) &-& (600s_700) &=& 0 |
||||
|
\end{array} \right. |
||||
|
$$ |
||||
|
|
||||
|
The format will be: |
||||
|
|
||||
|
```` |
||||
|
|
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 72 31 63 77 ┃ Magic |
||||
|
┣━━━━━━━━━━━━━━┫ |
||||
|
┃ 01 00 00 00 ┃ Version |
||||
|
┣━━━━━━━━━━━━━━┫ |
||||
|
┃ 04 00 00 00 ┃ nW |
||||
|
┣━━━━━━━━━━━━━━┫ |
||||
|
┃ 04 23 45 00 ┃ # of wires |
||||
|
┣━━━━━━━━━━━━━━┫ |
||||
|
┃ 01 00 00 00 ┃ # Public Outs |
||||
|
┣━━━━━━━━━━━━━━┫ |
||||
|
┃ 02 00 00 00 ┃ # Public Ins |
||||
|
┣━━━━━━━━━━━━━━┫ |
||||
|
┃ 05 00 00 00 ┃ # Private Ins |
||||
|
┗━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 03 00 00 00 ┃ # of constraints |
||||
|
┗━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
┏━━━━━━━━━━━━━━┓ Constraint 0: (3s_5 + 8s_6) * (2s_0 + 20s_2 + 12s_3) - (5s_0 + 7s_9) = 0 |
||||
|
┃ 02 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┓ |
||||
|
┃ 05 00 00 00 ┃ 01 03 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┫ |
||||
|
┃ 06 00 00 00 ┃ 01 08 ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━┛ |
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 03 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┓ |
||||
|
┃ 00 00 00 00 ┃ 01 02 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┫ |
||||
|
┃ 02 00 00 00 ┃ 01 14 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┫ |
||||
|
┃ 03 00 00 00 ┃ 01 0C ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━┛ |
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 02 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┓ |
||||
|
┃ 00 00 00 00 ┃ 01 05 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━┫ |
||||
|
┃ 09 00 00 00 ┃ 01 07 ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━┛ |
||||
|
|
||||
|
|
||||
|
┏━━━━━━━━━━━━━━┓ Constraint 1: (4s_1 + 8s_5 + 3s_9) * (6s_6 + 44s_3) = 0 |
||||
|
┃ 03 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓ |
||||
|
┃ 01 00 00 00 ┃ 01 04 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
||||
|
┃ 05 00 00 00 ┃ 01 08 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
||||
|
┃ 09 00 00 00 ┃ 01 03 ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛ |
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 02 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓ |
||||
|
┃ 03 00 00 00 ┃ 01 2C ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
||||
|
┃ 06 00 00 00 ┃ 01 06 ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛ |
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 00 00 00 00 ┃ |
||||
|
┗━━━━━━━━━━━━━━┛ |
||||
|
|
||||
|
|
||||
|
┏━━━━━━━━━━━━━━┓ Constraint 2: (4s_6) * (6s_0 + 5s_3 + 11s_9) - (600s_700) = 0 |
||||
|
┃ 01 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓ |
||||
|
┃ 06 00 00 00 ┃ 01 04 ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛ |
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 03 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┓ |
||||
|
┃ 00 00 00 00 ┃ 01 06 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
||||
|
┃ 03 00 00 00 ┃ 01 05 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━┫ |
||||
|
┃ 09 00 00 00 ┃ 01 0B ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━━┛ |
||||
|
┏━━━━━━━━━━━━━━┓ |
||||
|
┃ 01 00 00 00 ┃ |
||||
|
┣━━━━━━━━━━━━━━╋━━━━━━━━━━━━━┓ |
||||
|
┃ BC 02 00 00 ┃ 02 58 02 ┃ |
||||
|
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┛ |
||||
|
```` |
||||
|
|
||||
|
And the binary representation in Hex: |
||||
|
|
||||
|
```` |
||||
|
72 31 63 77 |
||||
|
01 00 00 00 |
||||
|
04 00 00 00 |
||||
|
04 23 45 00 |
||||
|
01 00 00 00 |
||||
|
02 00 00 00 |
||||
|
05 00 00 00 |
||||
|
03 00 00 00 |
||||
|
02 00 00 00 |
||||
|
05 00 00 00 01 03 |
||||
|
06 00 00 00 01 08 |
||||
|
03 00 00 00 |
||||
|
00 00 00 00 01 02 |
||||
|
02 00 00 00 01 14 |
||||
|
03 00 00 00 01 0C |
||||
|
02 00 00 00 |
||||
|
00 00 00 00 01 05 |
||||
|
09 00 00 00 01 07 |
||||
|
03 00 00 00 |
||||
|
01 00 00 00 01 04 |
||||
|
05 00 00 00 01 08 |
||||
|
09 00 00 00 01 03 |
||||
|
02 00 00 00 |
||||
|
03 00 00 00 01 2C |
||||
|
06 00 00 00 01 06 |
||||
|
00 00 00 00 |
||||
|
01 00 00 00 |
||||
|
06 00 00 00 01 04 |
||||
|
03 00 00 00 |
||||
|
00 00 00 00 01 06 |
||||
|
03 00 00 00 01 05 |
||||
|
09 00 00 00 01 0B |
||||
|
01 00 00 00 |
||||
|
BC 02 00 00 02 58 02 |
||||
|
```` |
||||
|
|
||||
|
## Implementation |
||||
|
|
||||
|
circom will output this format. |
||||
|
|
||||
|
## Copyright |
||||
|
|
||||
|
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
@ -1 +1,2 @@ |
|||||
module.exports = require("./src/compiler.js"); |
|
||||
|
module.exports.compiler = require("./src/compiler.js"); |
||||
|
module.exports.c_tester = require("./src/c_tester.js"); |
@ -0,0 +1,52 @@ |
|||||
|
|
||||
|
const ModuleBuilder = require("wasmbuilder").ModuleBuilder; |
||||
|
const gen = require("./gencode"); |
||||
|
|
||||
|
|
||||
|
module.exports = function buildWasm(ctx) { |
||||
|
|
||||
|
const fDefined = {}; |
||||
|
|
||||
|
ctx.module = new ModuleBuilder(); |
||||
|
for (let f in ctx.functions) { |
||||
|
ctx.f = ctx.module.addFunction(f); |
||||
|
ctx.c = ctx.f.getCodeBuilder(); |
||||
|
|
||||
|
ctx.scope = {}; |
||||
|
for (let p in ctx.functions[f].params) { |
||||
|
const param = ctx.functions[f].params[p]; |
||||
|
ctx.f.addParam(param.name, "i32"); |
||||
|
ctx.scope[param.name] = { |
||||
|
type: "PARAM", |
||||
|
sels: param.sels, |
||||
|
getter: () => { return ctx.c.getLocal(param.name); }, |
||||
|
setter: (v) => { return ctx.c.setLocal(param.name, v); } |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
gen(ctx, ctx.functions[f].block); |
||||
|
} |
||||
|
|
||||
|
for (let i=0; i<ctx.components.length; i++) { |
||||
|
const h = hashComponentCall(ctx, i); |
||||
|
const fName = ctx.components[i].temlate+"_"+h; |
||||
|
if (!fDefined[fName]) { |
||||
|
|
||||
|
ctx.f = ctx.module.addFunction(fName); |
||||
|
ctx.c = ctx.f.getCodeBuilder(); |
||||
|
|
||||
|
ctx.scope = {}; |
||||
|
for (let p in ctx.components[i].params) { |
||||
|
ctx.scope[p] = createConstant(ctx, ctx.components[i].params[p]); |
||||
|
} |
||||
|
|
||||
|
gen(ctx, ctx.templates[ctx.components[i].temlate].block); |
||||
|
} |
||||
|
ctx.components[i].f = fName; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
function buildSetSignal(ctx) { |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,377 @@ |
|||||
|
/* |
||||
|
Copyright 2018 0KIMS association. |
||||
|
|
||||
|
This file is part of circom (Zero Knowledge Circuit Compiler). |
||||
|
|
||||
|
circom is a free software: you can redistribute it and/or modify it |
||||
|
under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
circom is distributed in the hope that it will be useful, but WITHOUT |
||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
||||
|
License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
|
||||
|
const utils = require("./utils"); |
||||
|
const assert = require("assert"); |
||||
|
const gen = require("./c_gen"); |
||||
|
|
||||
|
module.exports = buildC; |
||||
|
|
||||
|
|
||||
|
function buildC(ctx) { |
||||
|
ctx.code = ""; |
||||
|
ctx.tmpNames = {}; |
||||
|
ctx.getTmpName = getTmpName; |
||||
|
ctx.codes_sizes = []; |
||||
|
ctx.definedSizes = {}; |
||||
|
ctx.addSizes = addSizes; |
||||
|
|
||||
|
const entryTables = buildEntryTables(ctx); |
||||
|
const code = buildCode(ctx); |
||||
|
const compnentsArray = buildComponentsArray(ctx); |
||||
|
|
||||
|
const headder = buildHeader(ctx); |
||||
|
const sizes = buildSizes(ctx); |
||||
|
const mapIsInput = buildMapIsInput(ctx); |
||||
|
const wit2Sig = buildWit2Sig(ctx); |
||||
|
const circuitVar = buildCircuitVar(ctx); |
||||
|
|
||||
|
return "" + |
||||
|
headder + "\n" + |
||||
|
sizes + "\n" + |
||||
|
entryTables + "\n" + |
||||
|
code + "\n" + |
||||
|
compnentsArray + "\n" + |
||||
|
mapIsInput + "\n" + |
||||
|
wit2Sig +"\n" + |
||||
|
circuitVar; |
||||
|
} |
||||
|
|
||||
|
function buildEntryTables(ctx) { |
||||
|
|
||||
|
const codes_hashMaps = []; |
||||
|
const codes_componentEntries = []; |
||||
|
const definedHashTables = {}; |
||||
|
for (let i=0; i<ctx.components.length; i++) { |
||||
|
const {htName, htMap} = addHashTable(i); |
||||
|
|
||||
|
let code = ""; |
||||
|
const componentEntriesTableName = ctx.getTmpName("entryTable_" + ctx.components[i].template); |
||||
|
|
||||
|
code += `Circom_ComponentEntry ${componentEntriesTableName}[${htMap.length}] = {\n`; |
||||
|
for (let j=0; j<htMap.length; j++) { |
||||
|
const entry = ctx.components[i].names.o[htMap[j]]; |
||||
|
code += j>0 ? " ," : " "; |
||||
|
const sizeName = ctx.addSizes(entry.sizes); |
||||
|
|
||||
|
const ty = entry.type == "S" ? "_typeSignal" : "_typeComponent"; |
||||
|
code += `{${entry.offset},${sizeName}, ${ty}}\n`; |
||||
|
} |
||||
|
code += "};\n"; |
||||
|
codes_componentEntries.push(code); |
||||
|
|
||||
|
ctx.components[i].htName = htName; |
||||
|
ctx.components[i].etName = componentEntriesTableName; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
return "" + |
||||
|
"// HashMaps\n" + |
||||
|
codes_hashMaps.join("\n") + "\n" + |
||||
|
"\n" + |
||||
|
"// Component Entries\n" + |
||||
|
codes_componentEntries.join("\n") + "\n" + |
||||
|
"\n"; |
||||
|
|
||||
|
function addHashTable(cIdx) { |
||||
|
const keys = Object.keys(ctx.components[cIdx].names.o); |
||||
|
assert(keys.length<128); |
||||
|
keys.sort((a,b) => ((a>b) ? 1 : -1)); |
||||
|
const h = utils.fnvHash(keys.join(",")); |
||||
|
if (definedHashTables[h]) return definedHashTables[h]; |
||||
|
definedHashTables[h] = {}; |
||||
|
definedHashTables[h].htName = ctx.getTmpName("ht_"+ctx.components[cIdx].template); |
||||
|
definedHashTables[h].htMap = []; |
||||
|
const t = []; |
||||
|
for (let i=0; i<keys.length; i++) { |
||||
|
definedHashTables[h].htMap[i] = keys[i]; |
||||
|
|
||||
|
const h2 = utils.fnvHash(keys[i]); |
||||
|
let pos = parseInt(h2.slice(-2), 16); |
||||
|
while (t[pos]) pos = (pos + 1) % 256; |
||||
|
t[pos] = [h2, i]; |
||||
|
} |
||||
|
let code = `Circom_HashEntry ${definedHashTables[h].htName}[256] = {`; |
||||
|
for (let i=0; i<256; i++) { |
||||
|
code += i>0 ? "," : ""; |
||||
|
if (t[i]) { |
||||
|
code += `{0x${t[i][0]}LL, ${t[i][1]}} /* ${keys[t[i][1]]} */`; |
||||
|
} else { |
||||
|
code += "{0,0}"; |
||||
|
} |
||||
|
} |
||||
|
code += "};\n"; |
||||
|
|
||||
|
codes_hashMaps.push(code); |
||||
|
|
||||
|
return definedHashTables[h]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function buildCode(ctx) { |
||||
|
const globalNames = ctx.tmpNames; |
||||
|
|
||||
|
const fDefined = {}; |
||||
|
|
||||
|
const functions = []; |
||||
|
for (let f in ctx.functions) { |
||||
|
ctx.scope = {}; |
||||
|
const paramsList = []; |
||||
|
for (let p in ctx.functions[f].params) { |
||||
|
const param = ctx.functions[f].params[p]; |
||||
|
paramsList.push("POINTER "+param.name); |
||||
|
|
||||
|
ctx.scope[param.name] = { |
||||
|
type: "LOCAL", |
||||
|
sels: param.sels, |
||||
|
getter: () => { return param.name; }, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
ctx.code += "void "+f+"(POINTER _ret, "+paramsList.join(",")+") {\n"; |
||||
|
|
||||
|
ctx.code += gen(ctx, ctx.functions[f].block); |
||||
|
ctx.code += "}"; |
||||
|
} |
||||
|
|
||||
|
for (let i=0; i<ctx.components.length; i++) { |
||||
|
const h = hashComponentCall(ctx, i); |
||||
|
const fName = ctx.components[i].template+"_"+h; |
||||
|
if (!fDefined[fName]) { |
||||
|
|
||||
|
|
||||
|
const scope = {_prefix : ""}; |
||||
|
ctx.scopes = [scope]; |
||||
|
ctx.nScopes = 0; |
||||
|
ctx.code = ""; |
||||
|
ctx.codeHeader = "// Header\n"; |
||||
|
ctx.codeFooter = "// Footer\n"; |
||||
|
ctx.tmpNames = Object.assign({},globalNames); |
||||
|
|
||||
|
for (let p in ctx.components[i].params) { |
||||
|
newRef(ctx, "BIGINT", p, ctx.components[i].params[p]); |
||||
|
} |
||||
|
|
||||
|
gen(ctx, ctx.templates[ctx.components[i].template].block); |
||||
|
|
||||
|
const S = `void ${fName}(Circom_CalcWit *ctx) {\n` + |
||||
|
utils.ident( |
||||
|
ctx.codeHeader + "\n" + |
||||
|
ctx.code + "\n" + |
||||
|
ctx.codeFooter |
||||
|
) + |
||||
|
"}\n"; |
||||
|
|
||||
|
functions.push(S); |
||||
|
} |
||||
|
ctx.components[i].fnName = fName; |
||||
|
} |
||||
|
|
||||
|
return functions.join("\n"); |
||||
|
} |
||||
|
|
||||
|
function buildComponentsArray(ctx) { |
||||
|
|
||||
|
const ccodes = []; |
||||
|
ccodes.push(`Circom_Component _components[${ctx.components.length}] = {\n`); |
||||
|
for (let i=0; i< ctx.components.length; i++) { |
||||
|
ccodes.push(i>0 ? " ," : " "); |
||||
|
ccodes.push(`{${ctx.components[i].htName},${ctx.components[i].etName},${ctx.components[i].fnName}, ${ctx.components[i].nInSignals}}\n`); |
||||
|
} |
||||
|
ccodes.push("};\n"); |
||||
|
const codeComponents = ccodes.join(""); |
||||
|
|
||||
|
return "" + |
||||
|
"// Components\n" + |
||||
|
codeComponents + |
||||
|
"\n"; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function buildHeader(ctx) { |
||||
|
return "#include \"circom.h\"\n" + |
||||
|
"#include \"calcwit.h\"\n" + |
||||
|
`#define NSignals ${ctx.signals.length}\n` + |
||||
|
`#define NComponents ${ctx.components.length}\n` + |
||||
|
`#define NInputs ${ctx.components[ ctx.getComponentIdx("main") ].nInSignals}\n`+ |
||||
|
`#define NOutputs ${ctx.totals[ ctx.stOUTPUT ]}\n`+ |
||||
|
`#define NVars ${ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL]}\n` + |
||||
|
"\n"; |
||||
|
} |
||||
|
|
||||
|
function buildSizes(ctx) { |
||||
|
return "// Sizes\n" + |
||||
|
ctx.codes_sizes.join("\n"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function buildMapIsInput(ctx) { |
||||
|
const arr = []; |
||||
|
let line = ""; |
||||
|
let acc = 0; |
||||
|
let i; |
||||
|
for (i=0; i<ctx.signals.length; i++) { |
||||
|
if (ctx.signals[i].o & ctx.IN) { |
||||
|
acc = acc | (1 << (i%32) ); |
||||
|
} |
||||
|
if ((i+1)%32==0) { |
||||
|
line += (i>31) ? "," : " "; |
||||
|
line += toHex(acc); |
||||
|
acc = 0; |
||||
|
if ( i % (32*64) == 0) { |
||||
|
arr.push(line); |
||||
|
line = ""; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ((i%32) != 0) { |
||||
|
line += (i>31) ? "," : " "; |
||||
|
line += toHex(acc); |
||||
|
} |
||||
|
if (line != "") { |
||||
|
arr.push(line); |
||||
|
} |
||||
|
|
||||
|
return "// mapIsArray\n" + |
||||
|
`u32 _mapIsInput[${Math.floor((ctx.signals.length-1) / 32)+1}] = {\n`+ |
||||
|
arr.join("\n") + "\n" + |
||||
|
"};\n"; |
||||
|
|
||||
|
function toHex(number) { |
||||
|
if (number < 0) number = 0xFFFFFFFF + number + 1; |
||||
|
let S=number.toString(16).toUpperCase(); |
||||
|
while (S.length<8) S = "0" + S; |
||||
|
return "0x"+S; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function buildWit2Sig(ctx) { |
||||
|
const codes = []; |
||||
|
const NVars = |
||||
|
ctx.totals[ctx.stONE] + |
||||
|
ctx.totals[ctx.stOUTPUT] + |
||||
|
ctx.totals[ctx.stPUBINPUT] + |
||||
|
ctx.totals[ctx.stPRVINPUT] + |
||||
|
ctx.totals[ctx.stINTERNAL]; |
||||
|
const arr = Array(NVars); |
||||
|
for (let i=0; i<ctx.signals.length; i++) { |
||||
|
const outIdx = ctx.signals[i].id; |
||||
|
if (typeof outIdx == "undefined") continue; |
||||
|
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
||||
|
assert(outIdx<NVars); |
||||
|
if (typeof arr[ctx.signals[i].id] == "undefined") { |
||||
|
arr[outIdx] = i; |
||||
|
} |
||||
|
} |
||||
|
codes.push("// Signal Table\n"); |
||||
|
codes.push(`int _wit2sig[${NVars}] = {\n`); |
||||
|
let code = ""; |
||||
|
for (let i=0; i<NVars; i++) { |
||||
|
code += (i>0) ? ",": " "; |
||||
|
code += arr[i]; |
||||
|
if ((i>0)&&(i%64 == 0)) { |
||||
|
if (code != "") codes.push(code + "\n"); |
||||
|
codes.push(code); |
||||
|
code =0; |
||||
|
} |
||||
|
} |
||||
|
if (code != "") codes.push(code + "\n"); |
||||
|
codes.push("};\n"); |
||||
|
|
||||
|
return codes.join(""); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
function buildCircuitVar() { |
||||
|
return "Circom_Circuit _circuit = {\n" + |
||||
|
" NSignals,\n"+ |
||||
|
" NComponents,\n"+ |
||||
|
" NInputs,\n"+ |
||||
|
" NOutputs,\n"+ |
||||
|
" NVars,\n"+ |
||||
|
" _wit2sig,\n"+ |
||||
|
" _components,\n"+ |
||||
|
" _mapIsInput\n"+ |
||||
|
"};\n"; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function hashComponentCall(ctx, cIdx) { |
||||
|
// TODO: At the moment generate a diferent function for each instance of the component
|
||||
|
return cIdx; |
||||
|
} |
||||
|
|
||||
|
function getTmpName(_suggestedName) { |
||||
|
let suggestedName; |
||||
|
if (_suggestedName) { |
||||
|
suggestedName = trimUnderscore(_suggestedName); |
||||
|
} else { |
||||
|
suggestedName = "tmp"; |
||||
|
} |
||||
|
|
||||
|
if (typeof(this.tmpNames[suggestedName]) == "undefined") { |
||||
|
this.tmpNames[suggestedName] = 1; |
||||
|
return "_"+suggestedName; |
||||
|
} else { |
||||
|
const name = "_" + suggestedName + "_" + this.tmpNames[suggestedName]; |
||||
|
this.tmpNames[suggestedName]++; |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
function trimUnderscore(str) { |
||||
|
let p1=0; |
||||
|
while ((p1 < str.length)&&(str[p1] == "_")) p1++; |
||||
|
let p2=str.length; |
||||
|
while ((p2 > 0)&&(str[p2-1] == "_")) p2--; |
||||
|
|
||||
|
return str.slice(p1,p2); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
function addSizes(_sizes) { |
||||
|
const sizes = _sizes || []; |
||||
|
let name = "sizes"; |
||||
|
for (let i=0; i<sizes.length;i++) { |
||||
|
name+="_"+sizes[i]; |
||||
|
} |
||||
|
if (name=="sizes") name="sizes_0"; |
||||
|
|
||||
|
if (this.definedSizes[name]) return this.definedSizes[name]; |
||||
|
|
||||
|
const labelName = this.getTmpName(name); |
||||
|
this.definedSizes[name] = labelName; |
||||
|
|
||||
|
const accSizes = utils.accSizes(sizes); |
||||
|
|
||||
|
let code = `Circom_Size ${labelName}[${accSizes.length}] = {`; |
||||
|
for (let i=0; i<accSizes.length; i++) { |
||||
|
if (i>0) code += ","; |
||||
|
code += accSizes[i]; |
||||
|
} |
||||
|
code += "};\n"; |
||||
|
this.codes_sizes.push(code); |
||||
|
|
||||
|
return labelName; |
||||
|
} |
||||
|
|
@ -0,0 +1,941 @@ |
|||||
|
const bigInt = require("big-integer"); |
||||
|
const utils = require("./utils"); |
||||
|
|
||||
|
module.exports = gen; |
||||
|
|
||||
|
|
||||
|
function newRef(ctx, type, _name, value, _sizes) { |
||||
|
const isValue = ((typeof(value) != "undefined")&&(value != null)); |
||||
|
let name; |
||||
|
let sizes; |
||||
|
if (!_name) { |
||||
|
name = ctx.getTmpName(); |
||||
|
} else { |
||||
|
if (_name[0] =="_") { |
||||
|
name = ctx.getTmpName(_name); |
||||
|
} else { |
||||
|
name = _name; |
||||
|
} |
||||
|
} |
||||
|
if (typeof(_sizes) == "string") { |
||||
|
sizes = _sizes; |
||||
|
} else if (Array.isArray(_sizes)) { |
||||
|
sizes = newSizes(ctx, _sizes); |
||||
|
} else if (isValue) { |
||||
|
sizes = newSizes(ctx, utils.extractSizes(value)); |
||||
|
} else { |
||||
|
sizes = newSizes(ctx, []); |
||||
|
} |
||||
|
|
||||
|
const scope = ctx.scopes[ctx.scopes.length-1]; |
||||
|
if (scope[name]) return error("Variable already exists: " + name); |
||||
|
|
||||
|
const label = scope._prefix + name; |
||||
|
scope[name] = { |
||||
|
stack: true, |
||||
|
type: type, |
||||
|
used: false, |
||||
|
sizes: sizes, |
||||
|
label: label |
||||
|
}; |
||||
|
|
||||
|
if (isValue) { |
||||
|
scope[name].value = value; |
||||
|
} |
||||
|
|
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
function newSizes(ctx, sizes) { |
||||
|
const scope = ctx.scopes[ctx.scopes.length-1]; |
||||
|
|
||||
|
const name = ctx.getTmpName("_sz"); |
||||
|
|
||||
|
scope[name] = { |
||||
|
stack: true, |
||||
|
type: "SIZES", |
||||
|
used: false, |
||||
|
dim: sizes.length, |
||||
|
label: name, |
||||
|
value: sizes |
||||
|
}; |
||||
|
|
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function instantiateRef(ctx, name) { |
||||
|
const v = getScope(ctx, name); |
||||
|
if (!v.stack) return error("Using a non existing var: " + name); |
||||
|
if (v.used) return; |
||||
|
|
||||
|
if (v.type=="BIGINT") { |
||||
|
|
||||
|
const iSize = getScope(ctx, v.sizes); |
||||
|
|
||||
|
if (iSize.used) { |
||||
|
const labelSize = iSize.label; |
||||
|
ctx.codeHeader += `PBigInt ${v.label};\n`; |
||||
|
ctx.code += `${v.label} = ctx->allocBigInts(${labelSize});\n`; |
||||
|
ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; |
||||
|
} else if (iSize.value) { |
||||
|
const labelSize = ctx.addSizes(iSize.value); |
||||
|
ctx.codeHeader += `PBigInt ${v.label} = ctx->allocBigInts(${labelSize});\n`; |
||||
|
ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; |
||||
|
} else { |
||||
|
return error(ctx, null, "Undefined Sizes: " +name); |
||||
|
} |
||||
|
|
||||
|
} else if (v.type=="INT") { |
||||
|
ctx.codeHeader += `int ${v.label};\n`; |
||||
|
} else if (v.type=="SIZES") { |
||||
|
ctx.codeHeader += `Circom_Sizes ${v.label};\n`; |
||||
|
} |
||||
|
v.used = true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function error(ctx, ast, errStr) { |
||||
|
ctx.error = { |
||||
|
pos: { |
||||
|
first_line: ast.first_line, |
||||
|
first_column: ast.first_column, |
||||
|
last_line: ast.last_line, |
||||
|
last_column: ast.last_column |
||||
|
}, |
||||
|
errStr: errStr, |
||||
|
ast: ast, |
||||
|
message: errStr |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function getScope(ctx, name) { |
||||
|
for (let i=ctx.scopes.length-1; i>=0; i--) { |
||||
|
if (ctx.scopes[i][name]) return ctx.scopes[i][name]; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function gen(ctx, ast) { |
||||
|
if ((ast.type == "NUMBER") ) { |
||||
|
return genNumber(ctx, ast); |
||||
|
} else if (ast.type == "VARIABLE") { |
||||
|
return genVariable(ctx, ast); |
||||
|
} else if (ast.type == "PIN") { |
||||
|
return genPin(ctx, ast); |
||||
|
} else if (ast.type == "OP") { |
||||
|
if (ast.op == "=") { |
||||
|
return genVarAssignement(ctx, ast); |
||||
|
} else if (ast.op == "<--") { |
||||
|
return genVarAssignement(ctx, ast); |
||||
|
} else if (ast.op == "<==") { |
||||
|
return genSignalAssignConstrain(ctx, ast); |
||||
|
} else if (ast.op == "===") { |
||||
|
return genConstraint(ctx, ast); |
||||
|
} else if (ast.op == "+=") { |
||||
|
return genVarAddAssignement(ctx, ast); |
||||
|
} else if (ast.op == "*=") { |
||||
|
return genVarMulAssignement(ctx, ast); |
||||
|
} else if (ast.op == "+") { |
||||
|
return genAdd(ctx, ast); |
||||
|
} else if (ast.op == "-") { |
||||
|
return genSub(ctx, ast); |
||||
|
} else if (ast.op == "UMINUS") { |
||||
|
return genUMinus(ctx, ast); |
||||
|
} else if (ast.op == "*") { |
||||
|
return genMul(ctx, ast); |
||||
|
} else if (ast.op == "%") { |
||||
|
return genMod(ctx, ast); |
||||
|
} else if (ast.op == "PLUSPLUSRIGHT") { |
||||
|
return genPlusPlusRight(ctx, ast); |
||||
|
} else if (ast.op == "PLUSPLUSLEFT") { |
||||
|
return genPlusPlusLeft(ctx, ast); |
||||
|
} else if (ast.op == "MINUSMINUSRIGHT") { |
||||
|
return genMinusMinusRight(ctx, ast); |
||||
|
} else if (ast.op == "MINUSMINUSLEFT") { |
||||
|
return genMinusMinusLeft(ctx, ast); |
||||
|
} else if (ast.op == "**") { |
||||
|
return genExp(ctx, ast); |
||||
|
} else if (ast.op == "/") { |
||||
|
return genDiv(ctx, ast); |
||||
|
} else if (ast.op == "\\") { |
||||
|
return genIDiv(ctx, ast); |
||||
|
} else if (ast.op == "&") { |
||||
|
return genBAnd(ctx, ast); |
||||
|
} else if (ast.op == "&&") { |
||||
|
return genAnd(ctx, ast); |
||||
|
} else if (ast.op == "||") { |
||||
|
return genOr(ctx, ast); |
||||
|
} else if (ast.op == "<<") { |
||||
|
return genShl(ctx, ast); |
||||
|
} else if (ast.op == ">>") { |
||||
|
return genShr(ctx, ast); |
||||
|
} else if (ast.op == "<") { |
||||
|
return genLt(ctx, ast); |
||||
|
} else if (ast.op == ">") { |
||||
|
return genGt(ctx, ast); |
||||
|
} else if (ast.op == "<=") { |
||||
|
return genLte(ctx, ast); |
||||
|
} else if (ast.op == ">=") { |
||||
|
return genGte(ctx, ast); |
||||
|
} else if (ast.op == "==") { |
||||
|
return genEq(ctx, ast); |
||||
|
} else if (ast.op == "!=") { |
||||
|
return genNeq(ctx, ast); |
||||
|
} else if (ast.op == "?") { |
||||
|
return genTerCon(ctx, ast); |
||||
|
} else { |
||||
|
error(ctx, ast, "Invalid operation: " + ast.op); |
||||
|
} |
||||
|
} else if (ast.type == "DECLARE") { |
||||
|
if (ast.declareType == "COMPONENT") { |
||||
|
return genDeclareComponent(ctx, ast); |
||||
|
} else if ((ast.declareType == "SIGNALIN")|| |
||||
|
(ast.declareType == "SIGNALOUT")|| |
||||
|
(ast.declareType == "SIGNAL")) { |
||||
|
return genDeclareSignal(ctx, ast); |
||||
|
} else if (ast.declareType == "VARIABLE") { |
||||
|
return genDeclareVariable(ctx, ast); |
||||
|
} else { |
||||
|
error(ctx, ast, "Invalid declaration: " + ast.declareType); |
||||
|
} |
||||
|
} else if (ast.type == "FUNCTIONCALL") { |
||||
|
return genFunctionCall(ctx, ast); |
||||
|
} else if (ast.type == "BLOCK") { |
||||
|
return genBlock(ctx, ast); |
||||
|
} else if (ast.type == "COMPUTE") { |
||||
|
return genCompute(ctx, ast); |
||||
|
} else if (ast.type == "FOR") { |
||||
|
return genFor(ctx, ast); |
||||
|
} else if (ast.type == "WHILE") { |
||||
|
return genWhile(ctx, ast); |
||||
|
} else if (ast.type == "IF") { |
||||
|
return genIf(ctx, ast); |
||||
|
} else if (ast.type == "RETURN") { |
||||
|
return genReturn(ctx, ast); |
||||
|
} else if (ast.type == "INCLUDE") { |
||||
|
return genInclude(ctx, ast); |
||||
|
} else if (ast.type == "ARRAY") { |
||||
|
return genArray(ctx, ast); |
||||
|
} else { |
||||
|
error(ctx, ast, "GEN -> Invalid AST node type: " + ast.type); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function genBlock(ctx, ast) { |
||||
|
const oldCode = ctx.code; |
||||
|
let res = null; |
||||
|
ctx.code = ""; |
||||
|
for (let i=0; i<ast.statements.length; i++) { |
||||
|
if (["BLOCK", "COMPUTE", "FOR", "WHILE", "IF"].indexOf(ast.statements[i].type)<0) { |
||||
|
genSrcComment(ctx, ast.statements[i]); |
||||
|
} |
||||
|
res = gen(ctx, ast.statements[i]); |
||||
|
if (ctx.error) return; |
||||
|
} |
||||
|
if (ctx.scopes.length>1) { |
||||
|
ctx.code = oldCode + `{\n${utils.ident(ctx.code)}}\n`; |
||||
|
} else { |
||||
|
ctx.code = oldCode + ctx.code; |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function genSrcComment(ctx, ast) { |
||||
|
let codes = []; |
||||
|
const fl= ast.first_line-1; |
||||
|
const ll = ast.last_line-1; |
||||
|
const fc = ast.first_column-1; |
||||
|
const lc = ast.last_column; |
||||
|
for (let i=fl; i<=ll; i++) { |
||||
|
const p1 = i==fl ? fc : 0; |
||||
|
const p2 = i==ll ? lc : -1; |
||||
|
codes.push(ctx.includedFiles[ctx.fileName][i].slice(p1, p2>=0 ? p2 : undefined)); |
||||
|
} |
||||
|
ctx.code += "\n/* "+codes.join("\n")+"*/\n"; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function genDeclareComponent(ctx, ast) { |
||||
|
const scope = ctx.scopes[ctx.scopes.length - 1]; |
||||
|
|
||||
|
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); |
||||
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); |
||||
|
|
||||
|
scope[ast.name.name] = { |
||||
|
type: "COMPONENT", |
||||
|
label: ast.name.name |
||||
|
}; |
||||
|
|
||||
|
return ast.name.name; |
||||
|
} |
||||
|
|
||||
|
function genDeclareSignal(ctx, ast) { |
||||
|
const scope = ctx.scopes[ctx.scopes.length-1]; |
||||
|
|
||||
|
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); |
||||
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); |
||||
|
|
||||
|
const res = { |
||||
|
type: "SIGNAL", |
||||
|
label: ast.name.name |
||||
|
}; |
||||
|
|
||||
|
scope[ast.name.name] = res; |
||||
|
|
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function genDeclareVariable(ctx, ast) { |
||||
|
|
||||
|
const scope = ctx.scopes[ctx.scopes.length-1]; |
||||
|
|
||||
|
const varName = ast.name.name; |
||||
|
const labelName = scope._prefix + ast.name.name; |
||||
|
|
||||
|
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); |
||||
|
if (ctx.scope[varName]) return error(ctx, ast, "Name already exists: "+varName); |
||||
|
|
||||
|
const sizes=[]; |
||||
|
let instantiate = false; |
||||
|
for (let i=0; i< ast.name.selectors.length; i++) { |
||||
|
const size = gen(ctx, ast.name.selectors[i]); |
||||
|
if (ctx.error) return; |
||||
|
|
||||
|
if (size.used) { |
||||
|
instantiate = true; |
||||
|
sizes.push(`BigInt2Int(${scope[size].label})`); |
||||
|
} else { |
||||
|
sizes.push(size.value.toJSNumber()); |
||||
|
} |
||||
|
|
||||
|
sizes.push( size.value.toJSNumber() ); |
||||
|
} |
||||
|
|
||||
|
const vSizes = newRef(ctx, "SIZES", "_sizes"); |
||||
|
const iSizes = scope[vSizes]; |
||||
|
iSizes.dim = ast.name.selectors.length; |
||||
|
if (instantiate) { |
||||
|
instantiateRef(ctx, vSizes); |
||||
|
ctx.code += `${iSizes.label}[${iSizes.dim+1}]=0`; |
||||
|
ctx.code += `${iSizes.label}[${iSizes.dim}]=1`; |
||||
|
for (let i=iSizes.dim-1; i>=0; i--) { |
||||
|
ctx.code += `${iSizes.label}[${i}] = ${sizes[i]}*${iSizes.label}[${i+1}];\n`; |
||||
|
} |
||||
|
} else { |
||||
|
iSizes.value = sizes; |
||||
|
} |
||||
|
|
||||
|
const res = ctx.scope[varName] = { |
||||
|
stack: true, |
||||
|
type: "BIGINT", |
||||
|
sizes: vSizes, |
||||
|
label: labelName |
||||
|
}; |
||||
|
|
||||
|
scope[varName] = res; |
||||
|
|
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function genNumber(ctx, ast) { |
||||
|
return newRef(ctx, "BIGINT", "_num", ast.value); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function genGetOffset(ctx, vOffset, vSizes, sels) { |
||||
|
|
||||
|
let rN = 0; |
||||
|
let rStr = ""; |
||||
|
let iOffset; |
||||
|
|
||||
|
if (vOffset) { |
||||
|
iOffset = getScope(ctx, vOffset); |
||||
|
if (iOffset.used) { |
||||
|
rStr += iOffset.label; |
||||
|
} else { |
||||
|
rN += iOffset.value.toJSNumber(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ((sels)&&(sels.length>0)) { |
||||
|
|
||||
|
const iSizes = getScope(ctx, vSizes); |
||||
|
|
||||
|
for (let i=0; i<sels.length; i++) { |
||||
|
const vIdx = gen(ctx, sels[i]); |
||||
|
const iIdx = getScope(ctx, vIdx); |
||||
|
|
||||
|
if ((!iIdx.used) && (!iSizes.used)) { |
||||
|
rN = rN + iIdx.value * iSizes.sizes[i+1]; |
||||
|
} else { |
||||
|
if (rN>0) { |
||||
|
if (rStr != "") rStr += " + "; |
||||
|
rStr += rN; |
||||
|
rN =0; |
||||
|
} |
||||
|
|
||||
|
if (rStr != "") rStr += " + "; |
||||
|
if (iIdx.used) { |
||||
|
rStr += vIdx; |
||||
|
} else { |
||||
|
rStr += iIdx.value; |
||||
|
} |
||||
|
rStr += "*"; |
||||
|
if (iSizes.used) { |
||||
|
rStr += `${iSizes.label}[${i+1}]`; |
||||
|
} else { |
||||
|
rStr += iSizes.sizes[i+1]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (rStr == "") { |
||||
|
const o = newRef(ctx, "INT", "_offset", rN); |
||||
|
return o; |
||||
|
} else { |
||||
|
if (rN>0) { |
||||
|
if (rStr != "") rStr += " + "; |
||||
|
rStr += rN; |
||||
|
rN =0; |
||||
|
} |
||||
|
if (rStr == iOffset.label) { |
||||
|
return vOffset; |
||||
|
} else { |
||||
|
const res = newRef(ctx, "INT", "_offset"); |
||||
|
instantiateRef(ctx, res); |
||||
|
ctx.code += `${res} = ${rStr};\n`; |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function genVariable(ctx, ast) { |
||||
|
const v = getScope(ctx, ast.name); |
||||
|
|
||||
|
|
||||
|
if (v.type == "SIGNAL") { |
||||
|
let vOffset; |
||||
|
if (ast.selectors.length>0) { |
||||
|
const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); |
||||
|
const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", ast.name); |
||||
|
vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors ); |
||||
|
} else { |
||||
|
vOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); |
||||
|
} |
||||
|
return genGetSignal(ctx, "ctx->cIdx", vOffset); |
||||
|
|
||||
|
} else if (v.type == "BIGINT") { |
||||
|
const vOffset = genGetOffset(ctx, 0, v.sizes, ast.sels ); |
||||
|
if (v.used) { |
||||
|
if (vOffset == 0) { |
||||
|
return ast.name; |
||||
|
} else { |
||||
|
const res = newRef(ctx, "BIGINT", "_v"); |
||||
|
instantiateRef(ctx, res); |
||||
|
ctx.code += `${res} = ${ast.name} + ${vOffset};\n`; |
||||
|
return res; |
||||
|
} |
||||
|
} else { |
||||
|
if (typeof(vOffset) == "string") { |
||||
|
instantiateRef(ctx, ast.name); |
||||
|
const vConstant = instantiateConstant(ctx, v.value); |
||||
|
const res = newRef(ctx, "BIGINT", "_v"); |
||||
|
instantiateRef(ctx, res); |
||||
|
ctx.code += `${res} = ${vConstant} + ${vOffset};\n`; |
||||
|
return res; |
||||
|
} else { |
||||
|
const sa = utils.subArray(v.value, ast.selectors); |
||||
|
const res = newRef(ctx, "BIGINT", "_v", sa); |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const sels = []; |
||||
|
for (let i=0; i<ast.selectors.length; i++) { |
||||
|
sels.push(gen(ctx, ast.selectors[i])); |
||||
|
if (ctx.error) return; |
||||
|
} |
||||
|
|
||||
|
if (!v) { |
||||
|
return error(ctx, ast, "Invalid left operand"); |
||||
|
} |
||||
|
if (v.type == "VARIABLE") { |
||||
|
return `ctx.getVar("${ast.name}",[${sels.join(",")}])`; |
||||
|
} else if (v.type == "SIGNAL") { |
||||
|
return `ctx.getSignal("${ast.name}", [${sels.join(",")}])`; |
||||
|
} else { |
||||
|
error(ctx, ast, "Invalid Variable type"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function instantiateConstant(ctx, value) { |
||||
|
const flatedValue = utils.flatArray(value); |
||||
|
const res = ctx.getTmpName("_const"); |
||||
|
ctx.codeHeader += `PBigInt ${res};\n`; |
||||
|
ctx.code += `${res.label} = ctx->allocBigInts(${flatedValue.length});\n`; |
||||
|
for (let i=0; i<flatedValue.length; i++) { |
||||
|
ctx.code += `mpz_init_set_str(${res.label}[${i}], ${flatedValue[i].toString(16)}, 16);\n`; |
||||
|
} |
||||
|
ctx.codeFooter += `ctx->freeBigInts(${res.label});\n`; |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function genPin(ctx, ast) { |
||||
|
let vcIdx; |
||||
|
if (ast.component.selectors.length>0) { |
||||
|
const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.component.name); |
||||
|
const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.component.name); |
||||
|
vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors ); |
||||
|
} else { |
||||
|
vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.component.name); |
||||
|
} |
||||
|
|
||||
|
let vsIdx; |
||||
|
if (ast.pin.selectors.length>0) { |
||||
|
const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.pin.name); |
||||
|
const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name); |
||||
|
vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors ); |
||||
|
} else { |
||||
|
vsIdx = genGetSigalOffset(ctx, vcIdx, ast.pin.name); |
||||
|
} |
||||
|
|
||||
|
return genGetSignal(ctx, vcIdx, vsIdx); |
||||
|
} |
||||
|
|
||||
|
function genGetSubComponentOffset(ctx, cIdx, label) { |
||||
|
const vOffset = newRef(ctx, "INT", "_compIdx"); |
||||
|
instantiateRef(ctx, vOffset); |
||||
|
|
||||
|
const h = utils.fnvHash(label); |
||||
|
ctx.code += `${vOffset} = ctx->getSubComponentOffset(${cIdx}, 0x${h}LL /* ${label} */);\n`; |
||||
|
return vOffset; |
||||
|
} |
||||
|
|
||||
|
function genGetSubComponentSizes(ctx, cIdx, label) { |
||||
|
const vSizes = newRef(ctx, "SIZES", "_compSizes"); |
||||
|
instantiateRef(ctx, vSizes); |
||||
|
|
||||
|
const h = utils.fnvHash(label); |
||||
|
ctx.code += `${vSizes} = ctx->getSubComponentSizes(${cIdx}, 0x${h}LL /* ${label} */);\n`; |
||||
|
return vSizes; |
||||
|
} |
||||
|
|
||||
|
function genGetSigalOffset(ctx, sIdx, label) { |
||||
|
const vOffset = newRef(ctx, "INT", "_sigIdx"); |
||||
|
instantiateRef(ctx, vOffset); |
||||
|
|
||||
|
const h = utils.fnvHash(label); |
||||
|
ctx.code += `${vOffset} = ctx->getSignalOffset(${sIdx}, 0x${h}LL /* ${label} */);\n`; |
||||
|
return vOffset; |
||||
|
} |
||||
|
|
||||
|
function genGetSignalSizes(ctx, sIdx, label) { |
||||
|
const vSizes = newRef(ctx, "SIZES", "_sigSizes"); |
||||
|
instantiateRef(ctx, vSizes); |
||||
|
|
||||
|
const h = utils.fnvHash(label); |
||||
|
ctx.code += `${vSizes} = ctx->getSignalSizes(${sIdx}, 0x${h}LL /* ${label} */);\n`; |
||||
|
return vSizes; |
||||
|
} |
||||
|
|
||||
|
function genSetSignal(ctx, cIdx, sIdx, value) { |
||||
|
ctx.code += `ctx->setSignal(${cIdx}, ${sIdx}, ${value});\n`; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
function genGetSignal(ctx, cIdx, sIdx) { |
||||
|
const res = newRef(ctx, "BIGINT", "_sigValue"); |
||||
|
instantiateRef(ctx, res); |
||||
|
ctx.code += `ctx->getSignal(${cIdx}, ${sIdx}, ${res});\n`; |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function genVarAssignement(ctx, ast) { |
||||
|
|
||||
|
let lName; |
||||
|
let sels; |
||||
|
|
||||
|
if (ctx.error) return; |
||||
|
|
||||
|
if (ast.values[0].type == "PIN") { |
||||
|
|
||||
|
let vcIdx; |
||||
|
if (ast.values[0].component.selectors.length>0) { |
||||
|
const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); |
||||
|
const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.values[0].component.name); |
||||
|
vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); |
||||
|
} else { |
||||
|
vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); |
||||
|
} |
||||
|
|
||||
|
let vsIdx; |
||||
|
if (ast.values[0].pin.selectors.length>0) { |
||||
|
const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); |
||||
|
const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); |
||||
|
vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); |
||||
|
} else { |
||||
|
vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); |
||||
|
} |
||||
|
|
||||
|
const vVal = gen(ctx, ast.values[1]); |
||||
|
|
||||
|
genSetSignal(ctx, vcIdx, vsIdx, vVal); |
||||
|
|
||||
|
return vVal; |
||||
|
} |
||||
|
|
||||
|
if (ast.values[0].type == "DECLARE") { |
||||
|
lName = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
sels = []; |
||||
|
} else { |
||||
|
lName = ast.values[0].name; |
||||
|
sels = ast.values[0].selectors; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const left = getScope(ctx, lName); |
||||
|
if (!left) return error(ctx, ast, "Variable does not exists: "+ast.values[0].name); |
||||
|
|
||||
|
// Component instantiation is already done.
|
||||
|
if (left.type == "COMPONENT") { |
||||
|
ctx.last = lName; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const rName = gen(ctx, ast.values[1]); |
||||
|
const right = getScope(ctx, rName); |
||||
|
|
||||
|
if (left.type == "SIGNAL") { |
||||
|
|
||||
|
let vsIdx; |
||||
|
if (sels.length>0) { |
||||
|
const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", lName); |
||||
|
const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", lName); |
||||
|
vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); |
||||
|
} else { |
||||
|
vsIdx = genGetSigalOffset(ctx, "ctx->cIdx", lName); |
||||
|
} |
||||
|
|
||||
|
return genSetSignal(ctx, "ctx->cIdx", vsIdx, rName); |
||||
|
} else if (left.type == "VAR") { |
||||
|
if (left.used) { |
||||
|
if (!right.used) { |
||||
|
instantiateRef(ctx, rName); |
||||
|
} |
||||
|
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; |
||||
|
return lName; |
||||
|
} else { |
||||
|
if (right.used) { |
||||
|
instantiateRef(ctx, lName); |
||||
|
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; |
||||
|
return lName; |
||||
|
} else { |
||||
|
if (!left.value) { |
||||
|
left.value = right.value; |
||||
|
} else { |
||||
|
if (!left.value.equals(right.value)) { |
||||
|
if (ctx.scopes.length > 1) { |
||||
|
instantiateRef(ctx, lName); |
||||
|
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`; |
||||
|
} else { |
||||
|
left.value = right.value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
return error(ctx, ast, "Assigning to invalid"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function genConstraint(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column; |
||||
|
ctx.code += `ctx->checkConstraint(${a}, ${b}, "${strErr}");`; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function genArray(ctx, ast) { |
||||
|
let S = "["; |
||||
|
for (let i=0; i<ast.values.length; i++) { |
||||
|
if (i>0) S += ","; |
||||
|
S += gen(ctx, ast.values[i]); |
||||
|
} |
||||
|
S+="]"; |
||||
|
return S; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function genFunctionCall(ctx, ast) { |
||||
|
let S = "["; |
||||
|
for (let i=0; i<ast.params.length; i++) { |
||||
|
if (i>0) S += ","; |
||||
|
S += gen(ctx, ast.params[i]); |
||||
|
} |
||||
|
S+="]"; |
||||
|
|
||||
|
return `ctx.callFunction("${ast.name}", ${S})`; |
||||
|
} |
||||
|
|
||||
|
function genFor(ctx, ast) { |
||||
|
ctx.scopes.push({}); |
||||
|
const init = gen(ctx, ast.init); |
||||
|
if (ctx.error) return; |
||||
|
const condition = gen(ctx, ast.condition); |
||||
|
if (ctx.error) return; |
||||
|
const step = gen(ctx, ast.step); |
||||
|
if (ctx.error) return; |
||||
|
const body = gen(ctx, ast.body); |
||||
|
if (ctx.error) return; |
||||
|
ctx.scopes.pop(); |
||||
|
return `for (${init};bigInt(${condition}).neq(bigInt(0));${step}) { \n${body}\n }\n`; |
||||
|
} |
||||
|
|
||||
|
function genWhile(ctx, ast) { |
||||
|
const condition = gen(ctx, ast.condition); |
||||
|
if (ctx.error) return; |
||||
|
const body = gen(ctx, ast.body); |
||||
|
if (ctx.error) return; |
||||
|
return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`; |
||||
|
} |
||||
|
|
||||
|
function genCompute(ctx, ast) { |
||||
|
const body = gen(ctx, ast.body); |
||||
|
if (ctx.error) return; |
||||
|
return `{\n${body}\n}\n`; |
||||
|
} |
||||
|
|
||||
|
function genIf(ctx, ast) { |
||||
|
const condition = gen(ctx, ast.condition); |
||||
|
if (ctx.error) return; |
||||
|
const thenBody = gen(ctx, ast.then); |
||||
|
if (ctx.error) return; |
||||
|
if (ast.else) { |
||||
|
const elseBody = gen(ctx, ast.else); |
||||
|
if (ctx.error) return; |
||||
|
return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n} else {\n${elseBody}\n}\n`; |
||||
|
} else { |
||||
|
return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n}\n`; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function genReturn(ctx, ast) { |
||||
|
const value = gen(ctx, ast.value); |
||||
|
if (ctx.error) return; |
||||
|
return `return ${value};`; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function genSignalAssignConstrain(ctx, ast) { |
||||
|
const res = genVarAssignement(ctx, ast); |
||||
|
genConstraint(ctx, ast); |
||||
|
return res; |
||||
|
// return genVarAssignement(ctx, ast);
|
||||
|
} |
||||
|
|
||||
|
function genVarAddAssignement(ctx, ast) { |
||||
|
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); |
||||
|
} |
||||
|
|
||||
|
function genVarMulAssignement(ctx, ast) { |
||||
|
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); |
||||
|
} |
||||
|
|
||||
|
function genPlusPlusRight(ctx, ast) { |
||||
|
return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genPlusPlusLeft(ctx, ast) { |
||||
|
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); |
||||
|
} |
||||
|
|
||||
|
function genMinusMinusRight(ctx, ast) { |
||||
|
return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genMinusMinusLeft(ctx, ast) { |
||||
|
return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); |
||||
|
} |
||||
|
|
||||
|
function genAdd(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).add(bigInt(${b})).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genMul(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).mul(bigInt(${b})).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genSub(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).add(__P__).sub(bigInt(${b})).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genDiv(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
|
||||
|
return `bigInt(${a}).mul( bigInt(${b}).inverse(__P__) ).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genIDiv(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
|
||||
|
return `bigInt(${a}).div( bigInt(${b}))`; |
||||
|
} |
||||
|
|
||||
|
function genExp(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).modPow(bigInt(${b}), __P__)`; |
||||
|
} |
||||
|
|
||||
|
function genBAnd(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`; |
||||
|
} |
||||
|
|
||||
|
function genAnd(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; |
||||
|
} |
||||
|
|
||||
|
function genOr(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; |
||||
|
} |
||||
|
|
||||
|
function genShl(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`; |
||||
|
} |
||||
|
|
||||
|
function genShr(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`; |
||||
|
} |
||||
|
|
||||
|
function genMod(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).mod(bigInt(${b}))`; |
||||
|
} |
||||
|
|
||||
|
function genLt(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`; |
||||
|
} |
||||
|
|
||||
|
function genGt(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).gt(bigInt(${b})) ? 1 : 0`; |
||||
|
} |
||||
|
|
||||
|
function genLte(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).lesserOrEquals(bigInt(${b})) ? 1 : 0`; |
||||
|
} |
||||
|
|
||||
|
function genGte(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).greaterOrEquals(bigInt(${b})) ? 1 : 0`; |
||||
|
} |
||||
|
|
||||
|
function genEq(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `(bigInt(${a}).eq(bigInt(${b})) ? 1 : 0)`; |
||||
|
} |
||||
|
|
||||
|
function genNeq(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
return `(bigInt(${a}).eq(bigInt(${b})) ? 0 : 1)`; |
||||
|
} |
||||
|
|
||||
|
function genUMinus(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
return `__P__.sub(bigInt(${a})).mod(__P__)`; |
||||
|
} |
||||
|
|
||||
|
function genTerCon(ctx, ast) { |
||||
|
const a = gen(ctx, ast.values[0]); |
||||
|
if (ctx.error) return; |
||||
|
const b = gen(ctx, ast.values[1]); |
||||
|
if (ctx.error) return; |
||||
|
const c = gen(ctx, ast.values[2]); |
||||
|
if (ctx.error) return; |
||||
|
return `bigInt(${a}).neq(bigInt(0)) ? (${b}) : (${c})`; |
||||
|
} |
||||
|
|
||||
|
function genInclude(ctx, ast) { |
||||
|
return ast.block ? gen(ctx, ast.block) : ""; |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,119 @@ |
|||||
|
const chai = require("chai"); |
||||
|
const assert = chai.assert; |
||||
|
|
||||
|
const fs = require("fs"); |
||||
|
var tmp = require("tmp-promise"); |
||||
|
const path = require("path"); |
||||
|
const compiler = require("./compiler"); |
||||
|
const util = require("util"); |
||||
|
const exec = util.promisify(require("child_process").exec); |
||||
|
|
||||
|
const stringifyBigInts = require("snarkjs").stringifyBigInts; |
||||
|
const unstringifyBigInts = require("snarkjs").unstringifyBigInts; |
||||
|
const bigInt = require("snarkjs").bigInt; |
||||
|
|
||||
|
module.exports = c_tester; |
||||
|
|
||||
|
|
||||
|
async function c_tester(circomFile, mainComponent, _options) { |
||||
|
tmp.setGracefulCleanup(); |
||||
|
mainComponent = mainComponent || "main"; |
||||
|
|
||||
|
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true }); |
||||
|
|
||||
|
const baseName = path.basename(circomFile, ".circom"); |
||||
|
const options = Object.assign({}, _options); |
||||
|
|
||||
|
options.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp")); |
||||
|
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym")); |
||||
|
options.mainComponent = mainComponent; |
||||
|
await compiler(circomFile, options); |
||||
|
|
||||
|
const cdir = path.join(__dirname, "..", "c"); |
||||
|
await exec("g++" + |
||||
|
` ${path.join(dir.path, baseName + ".cpp")} ` + |
||||
|
` ${path.join(cdir, "main.cpp")}` + |
||||
|
` ${path.join(cdir, "calcwit.cpp")}` + |
||||
|
` ${path.join(cdir, "utils.cpp")}` + |
||||
|
` -o ${path.join(dir.path, baseName)}` + |
||||
|
` -I ${cdir}` + |
||||
|
" -lgmp -std=c++11 -DSANITY_CHECK" |
||||
|
); |
||||
|
|
||||
|
console.log(dir.path); |
||||
|
return new CTester(dir, baseName, mainComponent); |
||||
|
} |
||||
|
|
||||
|
class CTester { |
||||
|
|
||||
|
constructor(dir, baseName, mainComponent) { |
||||
|
this.dir=dir; |
||||
|
this.baseName = baseName; |
||||
|
this.mainComponent = mainComponent; |
||||
|
} |
||||
|
|
||||
|
async release() { |
||||
|
await this.dir.cleanup(); |
||||
|
} |
||||
|
|
||||
|
async calculateWitness(input) { |
||||
|
await fs.promises.writeFile( |
||||
|
path.join(this.dir.path, "in.json"), |
||||
|
JSON.stringify(stringifyBigInts(input), null, 1) |
||||
|
); |
||||
|
await exec(`${path.join(this.dir.path, this.baseName)}` + |
||||
|
` ${path.join(this.dir.path, "in.json")}` + |
||||
|
` ${path.join(this.dir.path, "out.json")}` |
||||
|
); |
||||
|
const resStr = await fs.promises.readFile( |
||||
|
path.join(this.dir.path, "out.json") |
||||
|
); |
||||
|
|
||||
|
const res = unstringifyBigInts(JSON.parse(resStr)); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
async _loadSymbols() { |
||||
|
this.symbols = {}; |
||||
|
const symsStr = await fs.promises.readFile( |
||||
|
path.join(this.dir.path, this.baseName + ".sym"), |
||||
|
"utf8" |
||||
|
); |
||||
|
const lines = symsStr.split("\n"); |
||||
|
for (let i=0; i<lines.length; i++) { |
||||
|
const arr = lines[i].split(","); |
||||
|
if (arr.length!=3) continue; |
||||
|
this.symbols[arr[2]] = { |
||||
|
idx: Number(arr[0]), |
||||
|
idxWit: Number(arr[1]) |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async assertOut(actualOut, expectedOut) { |
||||
|
const self = this; |
||||
|
if (!self.symbols) await self._loadSymbols(); |
||||
|
|
||||
|
checkObject("main", expectedOut); |
||||
|
|
||||
|
function checkObject(prefix, eOut) { |
||||
|
|
||||
|
if (Array.isArray(eOut)) { |
||||
|
for (let i=0; i<eOut.length; i++) { |
||||
|
checkObject(prefix + "["+i+"]", eOut[i]); |
||||
|
} |
||||
|
} else if (typeof eOut == "object") { |
||||
|
for (let k in eOut) { |
||||
|
checkObject(prefix + "."+k, eOut[k]); |
||||
|
} |
||||
|
} else { |
||||
|
const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString(); |
||||
|
const be = bigInt(eOut).toString(); |
||||
|
assert.strictEqual(ba, be, prefix); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,171 @@ |
|||||
|
const bigInt = require("big-integer"); |
||||
|
|
||||
|
|
||||
|
class TableName { |
||||
|
constructor (ctx) { |
||||
|
this.ctx = ctx; |
||||
|
this.o = {}; |
||||
|
} |
||||
|
|
||||
|
_allocElement(name, _sizes, type) { |
||||
|
const sizes = _sizes || []; |
||||
|
let l = 1; |
||||
|
for (let i=0; i<sizes.length; i++) { |
||||
|
l = l*sizes[i]; |
||||
|
} |
||||
|
this.o[name] = { |
||||
|
sizes: sizes, |
||||
|
type: type |
||||
|
}; |
||||
|
return l; |
||||
|
} |
||||
|
|
||||
|
addSignal(name, sizes) { |
||||
|
const l = this._allocElement(name, sizes, "S"); |
||||
|
const o = this.ctx.nSignals; |
||||
|
this.o[name].offset = o; |
||||
|
this.ctx.nSignals += l; |
||||
|
if (l>1) { |
||||
|
return [o, o+l]; |
||||
|
} else { |
||||
|
return o; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addComponent(name, sizes) { |
||||
|
const l = this._allocElement(name, sizes, "C"); |
||||
|
const o = this.ctx.nComponents; |
||||
|
this.o[name].offset = o; |
||||
|
this.ctx.nComponents += l; |
||||
|
if (l>1) { |
||||
|
return [o, o+l]; |
||||
|
} else { |
||||
|
return o; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_getElement(name, _sels, type) { |
||||
|
const sels = _sels || []; |
||||
|
const s = this.o[name]; |
||||
|
if (!s) return -1; |
||||
|
if (s.type != type) return -1; |
||||
|
if (sels.length > s.sizes.length) return -1; |
||||
|
let l=1; |
||||
|
for (let i = s.sizes.length-1; i>sels.length; i--) { |
||||
|
l = l*s.sizes[i]; |
||||
|
} |
||||
|
let o =0; |
||||
|
let p=1; |
||||
|
for (let i=sels.length-1; i>=0; i--) { |
||||
|
if (sels[i] > s.sizes[i]) return -1; // Out of range
|
||||
|
if (sels[i] < 0) return -1; // Out of range
|
||||
|
o += p*sels[i]; |
||||
|
p *= s.sizes[i]; |
||||
|
} |
||||
|
if (l>1) { |
||||
|
return [s.offset + o, s.offset + o + l]; |
||||
|
} else { |
||||
|
return s.offset + o; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getSignalIdx(name, sels) { |
||||
|
return this._getElement(name, sels, "S"); |
||||
|
} |
||||
|
|
||||
|
getComponentIdx(name, sels) { |
||||
|
return this._getElement(name, sels, "C"); |
||||
|
} |
||||
|
|
||||
|
getSizes(name) { |
||||
|
return this.o[name].sels; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
module.exports = class Ctx { |
||||
|
|
||||
|
constructor() { |
||||
|
|
||||
|
this.stONE = 1; |
||||
|
this.stOUTPUT = 2; |
||||
|
this.stPUBINPUT = 3; |
||||
|
this.stPRVINPUT = 4; |
||||
|
this.stINTERNAL = 5; |
||||
|
this.stDISCARDED = 6; |
||||
|
this.stCONSTANT = 7; |
||||
|
|
||||
|
this.IN = 0x01; |
||||
|
this.OUT = 0x02; |
||||
|
this.PRV = 0x04; |
||||
|
this.ONE = 0x08; |
||||
|
this.MAIN = 0x10; |
||||
|
this.COUNTED = 0x20; |
||||
|
|
||||
|
this.scopes = [{}]; |
||||
|
this.signals = []; |
||||
|
|
||||
|
this.currentComponent= -1; |
||||
|
this.constraints= []; |
||||
|
this.components= []; |
||||
|
this.templates= {}; |
||||
|
this.functions= {}; |
||||
|
this.functionParams= {}; |
||||
|
this.nSignals = 0; |
||||
|
this.nComponents =0; |
||||
|
this.names = new TableName(this); |
||||
|
this.main=false; |
||||
|
|
||||
|
const oneIdx = this.addSignal("one"); |
||||
|
this.signals[oneIdx] = { |
||||
|
v: bigInt(1), |
||||
|
o: this.ONE, |
||||
|
e: -1, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
addSignal(name, sizes) { |
||||
|
if (this.currentComponent>=0) { |
||||
|
return this.components[this.currentComponent].names.addSignal(name, sizes); |
||||
|
} else { |
||||
|
return this.names.addSignal(name, sizes); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addComponent(name, sizes) { |
||||
|
if (this.currentComponent>=0) { |
||||
|
return this.components[this.currentComponent].names.addComponent(name, sizes); |
||||
|
} else { |
||||
|
return this.names.addComponent(name, sizes); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getSignalIdx(name, sels) { |
||||
|
if (this.currentComponent>=0) { |
||||
|
return this.components[this.currentComponent].names.getSignalIdx(name, sels); |
||||
|
} else { |
||||
|
return this.names.getSignalIdx(name, sels); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getComponentIdx(name, sels) { |
||||
|
if (this.currentComponent>=0) { |
||||
|
return this.components[this.currentComponent].names.getComponentIdx(name, sels); |
||||
|
} else { |
||||
|
return this.names.getComponentIdx(name, sels); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getSizes(name) { |
||||
|
if (this.currentComponent>=0) { |
||||
|
return this.components[this.currentComponent].names.getSizes(name); |
||||
|
} else { |
||||
|
return this.names.getSizes(name); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
newTableName() { |
||||
|
return new TableName(this); |
||||
|
} |
||||
|
|
||||
|
}; |
@ -0,0 +1,55 @@ |
|||||
|
const fnv = require("fnv-plus"); |
||||
|
|
||||
|
module.exports.ident =ident; |
||||
|
|
||||
|
module.exports.extractSizes =extractSizes; |
||||
|
module.exports.csArr = csArr; |
||||
|
module.exports.subArray = subArray; |
||||
|
module.exports.accSizes = accSizes; |
||||
|
module.exports.fnvHash = fnvHash; |
||||
|
|
||||
|
function ident(text) { |
||||
|
let lines = text.split("\n"); |
||||
|
for (let i=0; i<lines.length; i++) { |
||||
|
if (lines[i]) lines[i] = " "+lines[i]; |
||||
|
} |
||||
|
return lines.join("\n"); |
||||
|
} |
||||
|
|
||||
|
function extractSizes (o) { |
||||
|
if (! Array.isArray(o)) return [1, 0]; |
||||
|
return [o.length, ...extractSizes(o[0])]; |
||||
|
} |
||||
|
|
||||
|
// Input [1,2,3]
|
||||
|
// Returns " ,1 ,2, 3"
|
||||
|
function csArr(_arr) { |
||||
|
let S = ""; |
||||
|
const arr = _arr || []; |
||||
|
for (let i=0; i<arr.length; i++) { |
||||
|
S = " ,"+arr[i]; |
||||
|
} |
||||
|
return S; |
||||
|
} |
||||
|
|
||||
|
function subArray(value, sels) { |
||||
|
if ((!sels) || (sels.length == 0)) return value; |
||||
|
|
||||
|
return subArray(value[sels[0]], sels.slice(1)); |
||||
|
} |
||||
|
|
||||
|
function accSizes(_sizes) { |
||||
|
const sizes = _sizes || []; |
||||
|
const accSizes = [1, 0]; |
||||
|
for (let i=sizes.length-1; i>=0; i--) { |
||||
|
accSizes.unshift(accSizes[0]*sizes[i]); |
||||
|
} |
||||
|
return accSizes; |
||||
|
} |
||||
|
|
||||
|
function fnvHash(str) { |
||||
|
return fnv.hash(str, 64).hex(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
|
@ -0,0 +1 @@ |
|||||
|
{ "in1": 1, "in2": [2,3], "in3":[[4,5],[6,7],[8,9]] } |
@ -1,18 +1,54 @@ |
|||||
template Internal() { |
template Internal() { |
||||
signal input in; |
|
||||
signal output out; |
|
||||
|
signal input in1; |
||||
|
signal input in2[2]; |
||||
|
signal input in3[3][2]; |
||||
|
|
||||
out <== in; |
|
||||
|
signal output out1; |
||||
|
signal output out2[2]; |
||||
|
signal output out3[3][2]; |
||||
|
|
||||
|
out1 <== in1; |
||||
|
out2[0] <== in2[0]; |
||||
|
out2[1] <== in2[1]; |
||||
|
|
||||
|
out3[0][0] <== in3[0][0]; |
||||
|
out3[0][1] <== in3[0][1]; |
||||
|
out3[1][0] <== in3[1][0]; |
||||
|
out3[1][1] <== in3[1][1]; |
||||
|
out3[2][0] <== in3[2][0]; |
||||
|
out3[2][1] <== in3[2][1]; |
||||
} |
} |
||||
|
|
||||
template InOut() { |
template InOut() { |
||||
signal input in; |
|
||||
signal output out; |
|
||||
|
signal input in1; |
||||
|
signal input in2[2]; |
||||
|
signal input in3[3][2]; |
||||
|
|
||||
|
signal output out1; |
||||
|
signal output out2[2]; |
||||
|
signal output out3[3][2]; |
||||
|
|
||||
component internal = Internal(); |
component internal = Internal(); |
||||
|
|
||||
internal.in <== in; |
|
||||
internal.out ==> out; |
|
||||
|
internal.in1 <== in1; |
||||
|
internal.in2[0] <== in2[0]; |
||||
|
internal.in2[1] <== in2[1]; |
||||
|
internal.in3[0][0] <== in3[0][0]; |
||||
|
internal.in3[0][1] <== in3[0][1]; |
||||
|
internal.in3[1][0] <== in3[1][0]; |
||||
|
internal.in3[1][1] <== in3[1][1]; |
||||
|
internal.in3[2][0] <== in3[2][0]; |
||||
|
internal.in3[2][1] <== in3[2][1]; |
||||
|
|
||||
|
internal.out1 ==> out1; |
||||
|
internal.out2[0] ==> out2[0]; |
||||
|
internal.out2[1] ==> out2[1]; |
||||
|
internal.out3[0][0] ==> out3[0][0]; |
||||
|
internal.out3[0][1] ==> out3[0][1]; |
||||
|
internal.out3[1][0] ==> out3[1][0]; |
||||
|
internal.out3[1][1] ==> out3[1][1]; |
||||
|
internal.out3[2][0] ==> out3[2][0]; |
||||
|
internal.out3[2][1] ==> out3[2][1]; |
||||
} |
} |
||||
|
|
||||
component main = InOut(); |
component main = InOut(); |
@ -0,0 +1,20 @@ |
|||||
|
const chai = require("chai"); |
||||
|
const path = require("path"); |
||||
|
|
||||
|
const c_tester = require("../index.js").c_tester; |
||||
|
const stringifyBigInts = require("snarkjs").stringifyBigInts; |
||||
|
|
||||
|
|
||||
|
describe("inout test", function () { |
||||
|
this.timeout(100000); |
||||
|
it("Should compile a code with vars inside a for", async () => { |
||||
|
const cir = await c_tester(path.join(__dirname, "circuits", "inout.circom")); |
||||
|
|
||||
|
const out = await cir.calculateWitness({in1: 1, in2: [2,3], in3:[[4,5], [6,7], [8,9]]}); |
||||
|
|
||||
|
// console.log(JSON.stringify(stringifyBigInts(out),null,1));
|
||||
|
await cir.assertOut(out, {out1: 1, out2: [2,3], out3: [[4,5], [6,7],[8,9]]} ); |
||||
|
|
||||
|
await cir.release(); |
||||
|
}); |
||||
|
}); |