Skip to content

Commit

Permalink
Merge pull request #14 from nalinbhardwaj/master
Browse files Browse the repository at this point in the history
Fix Witness Calculator to be compatible with Circom 2
  • Loading branch information
jbaylina authored Jan 17, 2022
2 parents 21182bb + 0f581ef commit f25b6c9
Show file tree
Hide file tree
Showing 3 changed files with 384 additions and 5 deletions.
195 changes: 193 additions & 2 deletions build/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ function fnvHash(str) {
return shash;
}

// Note that this pads zeros
function toArray32(s,size) {
const res = []; //new Uint32Array(size); //has no unshift
let rem = BigInt(s);
const radix = BigInt(0x100000000);
while (rem) {
res.unshift( Number(rem % radix));
rem = rem / radix;
}
if (size) {
var i = size - res.length;
while (i>0) {
res.unshift(0);
i--;
}
}
return res;
}

/* globals WebAssembly */

async function builder(code, options) {
Expand Down Expand Up @@ -83,6 +102,32 @@ async function builder(code, options) {
"memory": memory
},
runtime: {
exceptionHandler: function(code) {
let errStr;
if (code == 1) {
errStr = "Signal not found. ";
} else if (code == 2) {
errStr = "Too many signals set. ";
} else if (code == 3) {
errStr = "Signal already set. ";
} else if (code == 4) {
errStr = "Assert Failed. ";
} else if (code == 5) {
errStr = "Not enough memory. ";
} else {
errStr = "Unknown error.";
}
console.log("ERROR: ", code, errStr);
throw new Error(errStr);
},
showSharedRWMemory: function() {
const shared_rw_memory_size = instance.exports.getFieldNumLen32();
const arr = new Uint32Array(shared_rw_memory_size);
for (let j=0; j<shared_rw_memory_size; j++) {
arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
}
console.log(ffjavascript.Scalar.fromArray(arr, 0x100000000));
},
error: function(code, pstr, a,b,c,d) {
let errStr;
if (code == 7) {
Expand Down Expand Up @@ -133,7 +178,12 @@ async function builder(code, options) {
options.logFinishComponent
);

wc = new WitnessCalculator(memory, instance, sanityCheck);
if (typeof instance.exports.getVersion == 'function') {
// Only circom 2 WASMs implement versioning
wc = new WitnessCalculatorCircom2(instance, sanityCheck);
} else {
wc = new WitnessCalculatorCircom1(memory, instance, sanityCheck);
}
return wc;

function p2str(p) {
Expand All @@ -146,7 +196,7 @@ async function builder(code, options) {
return String.fromCharCode.apply(null, bytes);
}
}
class WitnessCalculator {
class WitnessCalculatorCircom1 {
constructor(memory, instance, sanityCheck) {
this.memory = memory;
this.i32 = new Uint32Array(memory.buffer);
Expand All @@ -172,6 +222,10 @@ class WitnessCalculator {
this.sanityCheck = sanityCheck;
}

circom_version() {
return 1;
}

async _doCalculateWitness(input, sanityCheck) {
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
const pSigOffset = this.allocInt();
Expand Down Expand Up @@ -317,4 +371,141 @@ class WitnessCalculator {
}
}

class WitnessCalculatorCircom2 {
constructor(instance, sanityCheck) {
this.instance = instance;

this.version = this.instance.exports.getVersion();
this.n32 = this.instance.exports.getFieldNumLen32();

this.instance.exports.getRawPrime();
const arr = new Array(this.n32);
for (let i=0; i<this.n32; i++) {
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
}
this.prime = ffjavascript.Scalar.fromArray(arr, 0x100000000);

this.witnessSize = this.instance.exports.getWitnessSize();

this.sanityCheck = sanityCheck;
}

circom_version() {
return this.instance.exports.getVersion();
}

async _doCalculateWitness(input, sanityCheck) {
//input is assumed to be a map from signals to arrays of bigints
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
const keys = Object.keys(input);
var input_counter = 0;
keys.forEach( (k) => {
const h = fnvHash(k);
const hMSB = parseInt(h.slice(0,8), 16);
const hLSB = parseInt(h.slice(8,16), 16);
const fArr = flatArray(input[k]);
for (let i=0; i<fArr.length; i++) {
const arrFr = toArray32(fArr[i],this.n32);
for (let j=0; j<this.n32; j++) {
this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
}
try {
this.instance.exports.setInputSignal(hMSB, hLSB,i);
input_counter++;
} catch (err) {
// console.log(`After adding signal ${i} of ${k}`)
throw new Error(err);
}
}

});
if (input_counter < this.instance.exports.getInputSize()) {
throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
}
}

async calculateWitness(input, sanityCheck) {
const w = [];

await this._doCalculateWitness(input, sanityCheck);

for (let i=0; i<this.witnessSize; i++) {
this.instance.exports.getWitness(i);
const arr = new Uint32Array(this.n32);
for (let j=0; j<this.n32; j++) {
arr[this.n32-1-j] = this.instance.exports.readSharedRWMemory(j);
}
w.push(fromArray32(arr));
}

return w;
}

async calculateWTNSBin(input, sanityCheck) {
const buff32 = new Uint32Array(this.witnessSize*this.n32+this.n32+11);
const buff = new Uint8Array( buff32.buffer);
await this._doCalculateWitness(input, sanityCheck);

//"wtns"
buff[0] = "w".charCodeAt(0);
buff[1] = "t".charCodeAt(0);
buff[2] = "n".charCodeAt(0);
buff[3] = "s".charCodeAt(0);

//version 2
buff32[1] = 2;

//number of sections: 2
buff32[2] = 2;

//id section 1
buff32[3] = 1;

const n8 = this.n32*4;
//id section 1 length in 64bytes
const idSection1length = 8 + n8;
const idSection1lengthHex = idSection1length.toString(16);
buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);

//this.n32
buff32[6] = n8;

//prime number
this.instance.exports.getRawPrime();

var pos = 7;
for (let j=0; j<this.n32; j++) {
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
}
pos += this.n32;

// witness size
buff32[pos] = this.witnessSize;
pos++;

//id section 2
buff32[pos] = 2;
pos++;

// section 2 length
const idSection2length = n8*this.witnessSize;
const idSection2lengthHex = idSection2length.toString(16);
buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);

pos += 2;
for (let i=0; i<this.witnessSize; i++) {
this.instance.exports.getWitness(i);
for (let j=0; j<this.n32; j++) {
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
}
pos += this.n32;
}

return buff;
}

}

exports.WitnessCalculatorBuilder = builder;
19 changes: 19 additions & 0 deletions js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,22 @@ export function fnvHash(str) {
shash = '0'.repeat(n).concat(shash);
return shash;
}

// Note that this pads zeros
export function toArray32(s,size) {
const res = []; //new Uint32Array(size); //has no unshift
let rem = BigInt(s);
const radix = BigInt(0x100000000);
while (rem) {
res.unshift( Number(rem % radix));
rem = rem / radix;
}
if (size) {
var i = size - res.length;
while (i>0) {
res.unshift(0);
i--;
}
}
return res;
}
Loading

0 comments on commit f25b6c9

Please sign in to comment.