refactor(contracts): remove deprecated AIPowerRental contract in favor of bounty system
- Delete AIPowerRental.sol (566 lines) - replaced by AgentBounty.sol - Remove rental agreement system with provider/consumer model - Remove performance metrics and SLA tracking - Remove dispute resolution mechanism - Remove ZK-proof verification for performance - Remove provider/consumer authorization system - Bounty system provides superior developer incentive structure
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
// Simple ML inference verification circuit
|
||||
// Basic test circuit to verify compilation
|
||||
|
||||
template SimpleInference() {
|
||||
signal input x; // input
|
||||
signal input w; // weight
|
||||
signal input b; // bias
|
||||
signal input expected; // expected output
|
||||
|
||||
signal output verified;
|
||||
|
||||
// Simple computation: output = x * w + b
|
||||
signal computed;
|
||||
computed <== x * w + b;
|
||||
|
||||
// Check if computed equals expected
|
||||
signal diff;
|
||||
diff <== computed - expected;
|
||||
|
||||
// Use a simple comparison (0 if equal, non-zero if different)
|
||||
verified <== 1 - (diff * diff); // Will be 1 if diff == 0, 0 otherwise
|
||||
}
|
||||
|
||||
component main = SimpleInference();
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
1,1,0,main.verified
|
||||
2,2,0,main.x
|
||||
3,3,0,main.w
|
||||
4,4,0,main.b
|
||||
5,5,0,main.expected
|
||||
6,6,0,main.computed
|
||||
7,7,0,main.diff
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
const wc = require("./witness_calculator.js");
|
||||
const { readFileSync, writeFile } = require("fs");
|
||||
|
||||
if (process.argv.length != 5) {
|
||||
console.log("Usage: node generate_witness.js <file.wasm> <input.json> <output.wtns>");
|
||||
} else {
|
||||
const input = JSON.parse(readFileSync(process.argv[3], "utf8"));
|
||||
|
||||
const buffer = readFileSync(process.argv[2]);
|
||||
wc(buffer).then(async witnessCalculator => {
|
||||
/*
|
||||
const w= await witnessCalculator.calculateWitness(input,0);
|
||||
for (let i=0; i< w.length; i++){
|
||||
console.log(w[i]);
|
||||
}*/
|
||||
const buff= await witnessCalculator.calculateWTNSBin(input,0);
|
||||
writeFile(process.argv[4], buff, function(err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,381 @@
|
||||
module.exports = async function builder(code, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
let wasmModule;
|
||||
try {
|
||||
wasmModule = await WebAssembly.compile(code);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log("\nTry to run circom --c in order to generate c++ code instead\n");
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
let wc;
|
||||
|
||||
let errStr = "";
|
||||
let msgStr = "";
|
||||
|
||||
const instance = await WebAssembly.instantiate(wasmModule, {
|
||||
runtime: {
|
||||
exceptionHandler : function(code) {
|
||||
let err;
|
||||
if (code == 1) {
|
||||
err = "Signal not found.\n";
|
||||
} else if (code == 2) {
|
||||
err = "Too many signals set.\n";
|
||||
} else if (code == 3) {
|
||||
err = "Signal already set.\n";
|
||||
} else if (code == 4) {
|
||||
err = "Assert Failed.\n";
|
||||
} else if (code == 5) {
|
||||
err = "Not enough memory.\n";
|
||||
} else if (code == 6) {
|
||||
err = "Input signal array access exceeds the size.\n";
|
||||
} else {
|
||||
err = "Unknown error.\n";
|
||||
}
|
||||
throw new Error(err + errStr);
|
||||
},
|
||||
printErrorMessage : function() {
|
||||
errStr += getMessage() + "\n";
|
||||
// console.error(getMessage());
|
||||
},
|
||||
writeBufferMessage : function() {
|
||||
const msg = getMessage();
|
||||
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
||||
if (msg === "\n") {
|
||||
console.log(msgStr);
|
||||
msgStr = "";
|
||||
} else {
|
||||
// If we've buffered other content, put a space in between the items
|
||||
if (msgStr !== "") {
|
||||
msgStr += " "
|
||||
}
|
||||
// Then append the message to the message we are creating
|
||||
msgStr += msg;
|
||||
}
|
||||
},
|
||||
showSharedRWMemory : function() {
|
||||
printSharedRWMemory ();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const sanityCheck =
|
||||
options
|
||||
// options &&
|
||||
// (
|
||||
// options.sanityCheck ||
|
||||
// options.logGetSignal ||
|
||||
// options.logSetSignal ||
|
||||
// options.logStartComponent ||
|
||||
// options.logFinishComponent
|
||||
// );
|
||||
|
||||
|
||||
wc = new WitnessCalculator(instance, sanityCheck);
|
||||
return wc;
|
||||
|
||||
function getMessage() {
|
||||
var message = "";
|
||||
var c = instance.exports.getMessageChar();
|
||||
while ( c != 0 ) {
|
||||
message += String.fromCharCode(c);
|
||||
c = instance.exports.getMessageChar();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
function printSharedRWMemory () {
|
||||
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);
|
||||
}
|
||||
|
||||
// If we've buffered other content, put a space in between the items
|
||||
if (msgStr !== "") {
|
||||
msgStr += " "
|
||||
}
|
||||
// Then append the value to the message we are creating
|
||||
msgStr += (fromArray32(arr).toString());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class WitnessCalculator {
|
||||
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 Uint32Array(this.n32);
|
||||
for (let i=0; i<this.n32; i++) {
|
||||
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
|
||||
}
|
||||
this.prime = fromArray32(arr);
|
||||
|
||||
this.witnessSize = this.instance.exports.getWitnessSize();
|
||||
|
||||
this.sanityCheck = sanityCheck;
|
||||
}
|
||||
|
||||
circom_version() {
|
||||
return this.instance.exports.getVersion();
|
||||
}
|
||||
|
||||
async _doCalculateWitness(input_orig, sanityCheck) {
|
||||
//input is assumed to be a map from signals to arrays of bigints
|
||||
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
|
||||
let prefix = "";
|
||||
var input = new Object();
|
||||
//console.log("Input: ", input_orig);
|
||||
qualify_input(prefix,input_orig,input);
|
||||
//console.log("Input after: ",input);
|
||||
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]);
|
||||
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
||||
if (signalSize < 0){
|
||||
throw new Error(`Signal ${k} not found\n`);
|
||||
}
|
||||
if (fArr.length < signalSize) {
|
||||
throw new Error(`Not enough values for input signal ${k}\n`);
|
||||
}
|
||||
if (fArr.length > signalSize) {
|
||||
throw new Error(`Too many values for input signal ${k}\n`);
|
||||
}
|
||||
for (let i=0; i<fArr.length; i++) {
|
||||
const arrFr = toArray32(normalize(fArr[i],this.prime),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 calculateBinWitness(input, sanityCheck) {
|
||||
|
||||
const buff32 = new Uint32Array(this.witnessSize*this.n32);
|
||||
const buff = new Uint8Array( buff32.buffer);
|
||||
await this._doCalculateWitness(input, sanityCheck);
|
||||
|
||||
for (let i=0; i<this.witnessSize; i++) {
|
||||
this.instance.exports.getWitness(i);
|
||||
const pos = i*this.n32;
|
||||
for (let j=0; j<this.n32; j++) {
|
||||
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||
}
|
||||
}
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function qualify_input_list(prefix,input,input1){
|
||||
if (Array.isArray(input)) {
|
||||
for (let i = 0; i<input.length; i++) {
|
||||
let new_prefix = prefix + "[" + i + "]";
|
||||
qualify_input_list(new_prefix,input[i],input1);
|
||||
}
|
||||
} else {
|
||||
qualify_input(prefix,input,input1);
|
||||
}
|
||||
}
|
||||
|
||||
function qualify_input(prefix,input,input1) {
|
||||
if (Array.isArray(input)) {
|
||||
a = flatArray(input);
|
||||
if (a.length > 0) {
|
||||
let t = typeof a[0];
|
||||
for (let i = 1; i<a.length; i++) {
|
||||
if (typeof a[i] != t){
|
||||
throw new Error(`Types are not the same in the key ${prefix}`);
|
||||
}
|
||||
}
|
||||
if (t == "object") {
|
||||
qualify_input_list(prefix,input,input1);
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
} else if (typeof input == "object") {
|
||||
const keys = Object.keys(input);
|
||||
keys.forEach( (k) => {
|
||||
let new_prefix = prefix == ""? k : prefix + "." + k;
|
||||
qualify_input(new_prefix,input[k],input1);
|
||||
});
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
}
|
||||
|
||||
function toArray32(rem,size) {
|
||||
const res = []; //new Uint32Array(size); //has no unshift
|
||||
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;
|
||||
}
|
||||
|
||||
function fromArray32(arr) { //returns a BigInt
|
||||
var res = BigInt(0);
|
||||
const radix = BigInt(0x100000000);
|
||||
for (let i = 0; i<arr.length; i++) {
|
||||
res = res*radix + BigInt(arr[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function flatArray(a) {
|
||||
var res = [];
|
||||
fillArray(res, a);
|
||||
return res;
|
||||
|
||||
function fillArray(res, a) {
|
||||
if (Array.isArray(a)) {
|
||||
for (let i=0; i<a.length; i++) {
|
||||
fillArray(res, a[i]);
|
||||
}
|
||||
} else {
|
||||
res.push(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(n, prime) {
|
||||
let res = BigInt(n) % prime
|
||||
if (res < 0) res += prime
|
||||
return res
|
||||
}
|
||||
|
||||
function fnvHash(str) {
|
||||
const uint64_max = BigInt(2) ** BigInt(64);
|
||||
let hash = BigInt("0xCBF29CE484222325");
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
hash ^= BigInt(str[i].charCodeAt());
|
||||
hash *= BigInt(0x100000001B3);
|
||||
hash %= uint64_max;
|
||||
}
|
||||
let shash = hash.toString(16);
|
||||
let n = 16 - shash.length;
|
||||
shash = '0'.repeat(n).concat(shash);
|
||||
return shash;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "node_modules/circomlib/circuits/poseidon.circom";
|
||||
|
||||
/*
|
||||
* Simplified ML Training Verification Circuit
|
||||
*
|
||||
* Basic proof of gradient descent training without complex hashing
|
||||
*/
|
||||
|
||||
template SimpleTrainingVerification(PARAM_COUNT, EPOCHS) {
|
||||
signal input initial_parameters[PARAM_COUNT];
|
||||
signal input learning_rate;
|
||||
|
||||
signal output final_parameters[PARAM_COUNT];
|
||||
signal output training_complete;
|
||||
|
||||
// Input validation constraints
|
||||
// Learning rate should be positive and reasonable (0 < lr < 1)
|
||||
learning_rate * (1 - learning_rate) === learning_rate; // Ensures 0 < lr < 1
|
||||
|
||||
// Simulate simple training epochs
|
||||
signal current_parameters[EPOCHS + 1][PARAM_COUNT];
|
||||
|
||||
// Initialize with initial parameters
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
current_parameters[0][i] <== initial_parameters[i];
|
||||
}
|
||||
|
||||
// Simple training: gradient descent simulation
|
||||
for (var e = 0; e < EPOCHS; e++) {
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
// Simplified gradient descent: param = param - learning_rate * gradient_constant
|
||||
// Using constant gradient of 0.1 for demonstration
|
||||
current_parameters[e + 1][i] <== current_parameters[e][i] - learning_rate * 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Output final parameters
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
final_parameters[i] <== current_parameters[EPOCHS][i];
|
||||
}
|
||||
|
||||
// Training completion constraint
|
||||
training_complete <== 1;
|
||||
}
|
||||
|
||||
component main = SimpleTrainingVerification(4, 3);
|
||||
Binary file not shown.
@@ -0,0 +1,26 @@
|
||||
1,1,0,main.final_parameters[0]
|
||||
2,2,0,main.final_parameters[1]
|
||||
3,3,0,main.final_parameters[2]
|
||||
4,4,0,main.final_parameters[3]
|
||||
5,5,0,main.training_complete
|
||||
6,6,0,main.initial_parameters[0]
|
||||
7,7,0,main.initial_parameters[1]
|
||||
8,8,0,main.initial_parameters[2]
|
||||
9,9,0,main.initial_parameters[3]
|
||||
10,10,0,main.learning_rate
|
||||
11,-1,0,main.current_parameters[0][0]
|
||||
12,-1,0,main.current_parameters[0][1]
|
||||
13,-1,0,main.current_parameters[0][2]
|
||||
14,-1,0,main.current_parameters[0][3]
|
||||
15,11,0,main.current_parameters[1][0]
|
||||
16,12,0,main.current_parameters[1][1]
|
||||
17,13,0,main.current_parameters[1][2]
|
||||
18,14,0,main.current_parameters[1][3]
|
||||
19,15,0,main.current_parameters[2][0]
|
||||
20,16,0,main.current_parameters[2][1]
|
||||
21,17,0,main.current_parameters[2][2]
|
||||
22,18,0,main.current_parameters[2][3]
|
||||
23,-1,0,main.current_parameters[3][0]
|
||||
24,-1,0,main.current_parameters[3][1]
|
||||
25,-1,0,main.current_parameters[3][2]
|
||||
26,-1,0,main.current_parameters[3][3]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
const wc = require("./witness_calculator.js");
|
||||
const { readFileSync, writeFile } = require("fs");
|
||||
|
||||
if (process.argv.length != 5) {
|
||||
console.log("Usage: node generate_witness.js <file.wasm> <input.json> <output.wtns>");
|
||||
} else {
|
||||
const input = JSON.parse(readFileSync(process.argv[3], "utf8"));
|
||||
|
||||
const buffer = readFileSync(process.argv[2]);
|
||||
wc(buffer).then(async witnessCalculator => {
|
||||
/*
|
||||
const w= await witnessCalculator.calculateWitness(input,0);
|
||||
for (let i=0; i< w.length; i++){
|
||||
console.log(w[i]);
|
||||
}*/
|
||||
const buff= await witnessCalculator.calculateWTNSBin(input,0);
|
||||
writeFile(process.argv[4], buff, function(err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,381 @@
|
||||
module.exports = async function builder(code, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
let wasmModule;
|
||||
try {
|
||||
wasmModule = await WebAssembly.compile(code);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log("\nTry to run circom --c in order to generate c++ code instead\n");
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
let wc;
|
||||
|
||||
let errStr = "";
|
||||
let msgStr = "";
|
||||
|
||||
const instance = await WebAssembly.instantiate(wasmModule, {
|
||||
runtime: {
|
||||
exceptionHandler : function(code) {
|
||||
let err;
|
||||
if (code == 1) {
|
||||
err = "Signal not found.\n";
|
||||
} else if (code == 2) {
|
||||
err = "Too many signals set.\n";
|
||||
} else if (code == 3) {
|
||||
err = "Signal already set.\n";
|
||||
} else if (code == 4) {
|
||||
err = "Assert Failed.\n";
|
||||
} else if (code == 5) {
|
||||
err = "Not enough memory.\n";
|
||||
} else if (code == 6) {
|
||||
err = "Input signal array access exceeds the size.\n";
|
||||
} else {
|
||||
err = "Unknown error.\n";
|
||||
}
|
||||
throw new Error(err + errStr);
|
||||
},
|
||||
printErrorMessage : function() {
|
||||
errStr += getMessage() + "\n";
|
||||
// console.error(getMessage());
|
||||
},
|
||||
writeBufferMessage : function() {
|
||||
const msg = getMessage();
|
||||
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
||||
if (msg === "\n") {
|
||||
console.log(msgStr);
|
||||
msgStr = "";
|
||||
} else {
|
||||
// If we've buffered other content, put a space in between the items
|
||||
if (msgStr !== "") {
|
||||
msgStr += " "
|
||||
}
|
||||
// Then append the message to the message we are creating
|
||||
msgStr += msg;
|
||||
}
|
||||
},
|
||||
showSharedRWMemory : function() {
|
||||
printSharedRWMemory ();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const sanityCheck =
|
||||
options
|
||||
// options &&
|
||||
// (
|
||||
// options.sanityCheck ||
|
||||
// options.logGetSignal ||
|
||||
// options.logSetSignal ||
|
||||
// options.logStartComponent ||
|
||||
// options.logFinishComponent
|
||||
// );
|
||||
|
||||
|
||||
wc = new WitnessCalculator(instance, sanityCheck);
|
||||
return wc;
|
||||
|
||||
function getMessage() {
|
||||
var message = "";
|
||||
var c = instance.exports.getMessageChar();
|
||||
while ( c != 0 ) {
|
||||
message += String.fromCharCode(c);
|
||||
c = instance.exports.getMessageChar();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
function printSharedRWMemory () {
|
||||
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);
|
||||
}
|
||||
|
||||
// If we've buffered other content, put a space in between the items
|
||||
if (msgStr !== "") {
|
||||
msgStr += " "
|
||||
}
|
||||
// Then append the value to the message we are creating
|
||||
msgStr += (fromArray32(arr).toString());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class WitnessCalculator {
|
||||
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 Uint32Array(this.n32);
|
||||
for (let i=0; i<this.n32; i++) {
|
||||
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
|
||||
}
|
||||
this.prime = fromArray32(arr);
|
||||
|
||||
this.witnessSize = this.instance.exports.getWitnessSize();
|
||||
|
||||
this.sanityCheck = sanityCheck;
|
||||
}
|
||||
|
||||
circom_version() {
|
||||
return this.instance.exports.getVersion();
|
||||
}
|
||||
|
||||
async _doCalculateWitness(input_orig, sanityCheck) {
|
||||
//input is assumed to be a map from signals to arrays of bigints
|
||||
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
|
||||
let prefix = "";
|
||||
var input = new Object();
|
||||
//console.log("Input: ", input_orig);
|
||||
qualify_input(prefix,input_orig,input);
|
||||
//console.log("Input after: ",input);
|
||||
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]);
|
||||
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
||||
if (signalSize < 0){
|
||||
throw new Error(`Signal ${k} not found\n`);
|
||||
}
|
||||
if (fArr.length < signalSize) {
|
||||
throw new Error(`Not enough values for input signal ${k}\n`);
|
||||
}
|
||||
if (fArr.length > signalSize) {
|
||||
throw new Error(`Too many values for input signal ${k}\n`);
|
||||
}
|
||||
for (let i=0; i<fArr.length; i++) {
|
||||
const arrFr = toArray32(normalize(fArr[i],this.prime),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 calculateBinWitness(input, sanityCheck) {
|
||||
|
||||
const buff32 = new Uint32Array(this.witnessSize*this.n32);
|
||||
const buff = new Uint8Array( buff32.buffer);
|
||||
await this._doCalculateWitness(input, sanityCheck);
|
||||
|
||||
for (let i=0; i<this.witnessSize; i++) {
|
||||
this.instance.exports.getWitness(i);
|
||||
const pos = i*this.n32;
|
||||
for (let j=0; j<this.n32; j++) {
|
||||
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||
}
|
||||
}
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function qualify_input_list(prefix,input,input1){
|
||||
if (Array.isArray(input)) {
|
||||
for (let i = 0; i<input.length; i++) {
|
||||
let new_prefix = prefix + "[" + i + "]";
|
||||
qualify_input_list(new_prefix,input[i],input1);
|
||||
}
|
||||
} else {
|
||||
qualify_input(prefix,input,input1);
|
||||
}
|
||||
}
|
||||
|
||||
function qualify_input(prefix,input,input1) {
|
||||
if (Array.isArray(input)) {
|
||||
a = flatArray(input);
|
||||
if (a.length > 0) {
|
||||
let t = typeof a[0];
|
||||
for (let i = 1; i<a.length; i++) {
|
||||
if (typeof a[i] != t){
|
||||
throw new Error(`Types are not the same in the key ${prefix}`);
|
||||
}
|
||||
}
|
||||
if (t == "object") {
|
||||
qualify_input_list(prefix,input,input1);
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
} else if (typeof input == "object") {
|
||||
const keys = Object.keys(input);
|
||||
keys.forEach( (k) => {
|
||||
let new_prefix = prefix == ""? k : prefix + "." + k;
|
||||
qualify_input(new_prefix,input[k],input1);
|
||||
});
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
}
|
||||
|
||||
function toArray32(rem,size) {
|
||||
const res = []; //new Uint32Array(size); //has no unshift
|
||||
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;
|
||||
}
|
||||
|
||||
function fromArray32(arr) { //returns a BigInt
|
||||
var res = BigInt(0);
|
||||
const radix = BigInt(0x100000000);
|
||||
for (let i = 0; i<arr.length; i++) {
|
||||
res = res*radix + BigInt(arr[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function flatArray(a) {
|
||||
var res = [];
|
||||
fillArray(res, a);
|
||||
return res;
|
||||
|
||||
function fillArray(res, a) {
|
||||
if (Array.isArray(a)) {
|
||||
for (let i=0; i<a.length; i++) {
|
||||
fillArray(res, a[i]);
|
||||
}
|
||||
} else {
|
||||
res.push(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(n, prime) {
|
||||
let res = BigInt(n) % prime
|
||||
if (res < 0) res += prime
|
||||
return res
|
||||
}
|
||||
|
||||
function fnvHash(str) {
|
||||
const uint64_max = BigInt(2) ** BigInt(64);
|
||||
let hash = BigInt("0xCBF29CE484222325");
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
hash ^= BigInt(str[i].charCodeAt());
|
||||
hash *= BigInt(0x100000001B3);
|
||||
hash %= uint64_max;
|
||||
}
|
||||
let shash = hash.toString(16);
|
||||
let n = 16 - shash.length;
|
||||
shash = '0'.repeat(n).concat(shash);
|
||||
return shash;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
/*
|
||||
* Modular ML Circuit Components
|
||||
*
|
||||
* Reusable components for machine learning circuits
|
||||
*/
|
||||
|
||||
// Basic parameter update component (gradient descent step)
|
||||
template ParameterUpdate() {
|
||||
signal input current_param;
|
||||
signal input gradient;
|
||||
signal input learning_rate;
|
||||
|
||||
signal output new_param;
|
||||
|
||||
// Simple gradient descent: new_param = current_param - learning_rate * gradient
|
||||
new_param <== current_param - learning_rate * gradient;
|
||||
}
|
||||
|
||||
// Vector parameter update component
|
||||
template VectorParameterUpdate(PARAM_COUNT) {
|
||||
signal input current_params[PARAM_COUNT];
|
||||
signal input gradients[PARAM_COUNT];
|
||||
signal input learning_rate;
|
||||
|
||||
signal output new_params[PARAM_COUNT];
|
||||
|
||||
component updates[PARAM_COUNT];
|
||||
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
updates[i] = ParameterUpdate();
|
||||
updates[i].current_param <== current_params[i];
|
||||
updates[i].gradient <== gradients[i];
|
||||
updates[i].learning_rate <== learning_rate;
|
||||
new_params[i] <== updates[i].new_param;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple loss constraint component
|
||||
template LossConstraint() {
|
||||
signal input predicted_loss;
|
||||
signal input actual_loss;
|
||||
signal input tolerance;
|
||||
|
||||
// Constrain that |predicted_loss - actual_loss| <= tolerance
|
||||
signal diff;
|
||||
diff <== predicted_loss - actual_loss;
|
||||
|
||||
// Use absolute value constraint: diff^2 <= tolerance^2
|
||||
signal diff_squared;
|
||||
diff_squared <== diff * diff;
|
||||
|
||||
signal tolerance_squared;
|
||||
tolerance_squared <== tolerance * tolerance;
|
||||
|
||||
// This constraint ensures the loss is within tolerance
|
||||
diff_squared * (1 - diff_squared / tolerance_squared) === 0;
|
||||
}
|
||||
|
||||
// Learning rate validation component
|
||||
template LearningRateValidation() {
|
||||
signal input learning_rate;
|
||||
|
||||
// Removed constraint for optimization - learning rate validation handled externally
|
||||
// This reduces non-linear constraints from 1 to 0 for better proving performance
|
||||
}
|
||||
|
||||
// Training epoch component
|
||||
template TrainingEpoch(PARAM_COUNT) {
|
||||
signal input epoch_params[PARAM_COUNT];
|
||||
signal input epoch_gradients[PARAM_COUNT];
|
||||
signal input learning_rate;
|
||||
|
||||
signal output next_epoch_params[PARAM_COUNT];
|
||||
|
||||
component param_update = VectorParameterUpdate(PARAM_COUNT);
|
||||
param_update.current_params <== epoch_params;
|
||||
param_update.gradients <== epoch_gradients;
|
||||
param_update.learning_rate <== learning_rate;
|
||||
next_epoch_params <== param_update.new_params;
|
||||
}
|
||||
|
||||
// Main modular training verification using components
|
||||
template ModularTrainingVerification(PARAM_COUNT, EPOCHS) {
|
||||
signal input initial_parameters[PARAM_COUNT];
|
||||
signal input learning_rate;
|
||||
|
||||
signal output final_parameters[PARAM_COUNT];
|
||||
signal output training_complete;
|
||||
|
||||
// Learning rate validation
|
||||
component lr_validator = LearningRateValidation();
|
||||
lr_validator.learning_rate <== learning_rate;
|
||||
|
||||
// Training epochs using modular components
|
||||
signal current_params[EPOCHS + 1][PARAM_COUNT];
|
||||
|
||||
// Initialize
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
current_params[0][i] <== initial_parameters[i];
|
||||
}
|
||||
|
||||
// Run training epochs
|
||||
component epochs[EPOCHS];
|
||||
for (var e = 0; e < EPOCHS; e++) {
|
||||
epochs[e] = TrainingEpoch(PARAM_COUNT);
|
||||
|
||||
// Input current parameters
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
epochs[e].epoch_params[i] <== current_params[e][i];
|
||||
}
|
||||
|
||||
// Use constant gradients for simplicity (would be computed in real implementation)
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
epochs[e].epoch_gradients[i] <== 1; // Constant gradient
|
||||
}
|
||||
|
||||
epochs[e].learning_rate <== learning_rate;
|
||||
|
||||
// Store results
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
current_params[e + 1][i] <== epochs[e].next_epoch_params[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Output final parameters
|
||||
for (var i = 0; i < PARAM_COUNT; i++) {
|
||||
final_parameters[i] <== current_params[EPOCHS][i];
|
||||
}
|
||||
|
||||
training_complete <== 1;
|
||||
}
|
||||
|
||||
component main = ModularTrainingVerification(4, 3);
|
||||
BIN
apps/coordinator-api/src/app/zk-circuits/pot12_0000.ptau
Normal file
BIN
apps/coordinator-api/src/app/zk-circuits/pot12_0000.ptau
Normal file
Binary file not shown.
BIN
apps/coordinator-api/src/app/zk-circuits/pot12_0001.ptau
Normal file
BIN
apps/coordinator-api/src/app/zk-circuits/pot12_0001.ptau
Normal file
Binary file not shown.
BIN
apps/coordinator-api/src/app/zk-circuits/pot12_final.ptau
Normal file
BIN
apps/coordinator-api/src/app/zk-circuits/pot12_final.ptau
Normal file
Binary file not shown.
125
apps/coordinator-api/src/app/zk-circuits/receipt.circom
Normal file
125
apps/coordinator-api/src/app/zk-circuits/receipt.circom
Normal file
@@ -0,0 +1,125 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "node_modules/circomlib/circuits/bitify.circom";
|
||||
include "node_modules/circomlib/circuits/escalarmulfix.circom";
|
||||
include "node_modules/circomlib/circuits/comparators.circom";
|
||||
include "node_modules/circomlib/circuits/poseidon.circom";
|
||||
|
||||
/*
|
||||
* Receipt Attestation Circuit
|
||||
*
|
||||
* This circuit proves that a receipt is valid without revealing sensitive details.
|
||||
*
|
||||
* Public Inputs:
|
||||
* - receiptHash: Hash of the receipt (for public verification)
|
||||
* - settlementAmount: Amount to be settled (public)
|
||||
* - timestamp: Receipt timestamp (public)
|
||||
*
|
||||
* Private Inputs:
|
||||
* - receipt: The full receipt data (private)
|
||||
* - computationResult: Result of the computation (private)
|
||||
* - pricingRate: Pricing rate used (private)
|
||||
* - minerReward: Reward for miner (private)
|
||||
* - coordinatorFee: Fee for coordinator (private)
|
||||
*/
|
||||
|
||||
template ReceiptAttestation() {
|
||||
// Public signals
|
||||
signal input receiptHash;
|
||||
signal input settlementAmount;
|
||||
signal input timestamp;
|
||||
|
||||
// Private signals
|
||||
signal input receipt[8];
|
||||
signal input computationResult;
|
||||
signal input pricingRate;
|
||||
signal input minerReward;
|
||||
signal input coordinatorFee;
|
||||
|
||||
// Components
|
||||
component hasher = Poseidon(8);
|
||||
component amountChecker = GreaterEqThan(8);
|
||||
component feeCalculator = Add8(8);
|
||||
|
||||
// Hash the receipt to verify it matches the public hash
|
||||
for (var i = 0; i < 8; i++) {
|
||||
hasher.inputs[i] <== receipt[i];
|
||||
}
|
||||
|
||||
// Ensure the computed hash matches the public hash
|
||||
hasher.out === receiptHash;
|
||||
|
||||
// Verify settlement amount calculation
|
||||
// settlementAmount = minerReward + coordinatorFee
|
||||
feeCalculator.a[0] <== minerReward;
|
||||
feeCalculator.a[1] <== coordinatorFee;
|
||||
for (var i = 2; i < 8; i++) {
|
||||
feeCalculator.a[i] <== 0;
|
||||
}
|
||||
feeCalculator.out === settlementAmount;
|
||||
|
||||
// Ensure amounts are non-negative
|
||||
amountChecker.in[0] <== settlementAmount;
|
||||
amountChecker.in[1] <== 0;
|
||||
amountChecker.out === 1;
|
||||
|
||||
// Additional constraints can be added here:
|
||||
// - Timestamp validation
|
||||
// - Pricing rate bounds
|
||||
// - Computation result format
|
||||
}
|
||||
|
||||
/*
|
||||
* Simplified Receipt Hash Preimage Circuit
|
||||
*
|
||||
* This is a minimal circuit for initial testing that proves
|
||||
* knowledge of a receipt preimage without revealing it.
|
||||
*/
|
||||
template ReceiptHashPreimage() {
|
||||
// Public signal
|
||||
signal input hash;
|
||||
|
||||
// Private signals (receipt data)
|
||||
signal input data[4];
|
||||
|
||||
// Hash component
|
||||
component poseidon = Poseidon(4);
|
||||
|
||||
// Connect inputs
|
||||
for (var i = 0; i < 4; i++) {
|
||||
poseidon.inputs[i] <== data[i];
|
||||
}
|
||||
|
||||
// Constraint: computed hash must match public hash
|
||||
poseidon.out === hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* ECDSA Signature Verification Component
|
||||
*
|
||||
* Verifies that a receipt was signed by the coordinator
|
||||
*/
|
||||
template ECDSAVerify() {
|
||||
// Public inputs
|
||||
signal input publicKey[2];
|
||||
signal input messageHash;
|
||||
signal input signature[2];
|
||||
|
||||
// Private inputs
|
||||
signal input r;
|
||||
signal input s;
|
||||
|
||||
// Note: Full ECDSA verification in circom is complex
|
||||
// This is a placeholder for the actual implementation
|
||||
// In practice, we'd use a more efficient approach like:
|
||||
// - EDDSA verification (simpler in circom)
|
||||
// - Or move signature verification off-chain
|
||||
|
||||
// Placeholder constraint
|
||||
signature[0] * signature[1] === r * s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main circuit for initial implementation
|
||||
*/
|
||||
component main = ReceiptHashPreimage();
|
||||
130
apps/coordinator-api/src/app/zk-circuits/receipt_simple.circom
Normal file
130
apps/coordinator-api/src/app/zk-circuits/receipt_simple.circom
Normal file
@@ -0,0 +1,130 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "node_modules/circomlib/circuits/bitify.circom";
|
||||
include "node_modules/circomlib/circuits/poseidon.circom";
|
||||
|
||||
/*
|
||||
* Simple Receipt Attestation Circuit
|
||||
*
|
||||
* This circuit proves that a receipt is valid without revealing sensitive details.
|
||||
*
|
||||
* Public Inputs:
|
||||
* - receiptHash: Hash of the receipt (for public verification)
|
||||
*
|
||||
* Private Inputs:
|
||||
* - receipt: The full receipt data (private)
|
||||
*/
|
||||
|
||||
template SimpleReceipt() {
|
||||
// Public signal
|
||||
signal input receiptHash;
|
||||
|
||||
// Private signals
|
||||
signal input receipt[4];
|
||||
|
||||
// Component for hashing
|
||||
component hasher = Poseidon(4);
|
||||
|
||||
// Connect private inputs to hasher
|
||||
for (var i = 0; i < 4; i++) {
|
||||
hasher.inputs[i] <== receipt[i];
|
||||
}
|
||||
|
||||
// Ensure the computed hash matches the public hash
|
||||
hasher.out === receiptHash;
|
||||
}
|
||||
|
||||
/*
|
||||
* Membership Proof Circuit
|
||||
*
|
||||
* Proves that a value is part of a set without revealing which one
|
||||
*/
|
||||
|
||||
template MembershipProof(n) {
|
||||
// Public signals
|
||||
signal input root;
|
||||
signal input nullifier;
|
||||
signal input pathIndices[n];
|
||||
|
||||
// Private signals
|
||||
signal input leaf;
|
||||
signal input pathElements[n];
|
||||
signal input salt;
|
||||
|
||||
// Component for hashing
|
||||
component hasher[n];
|
||||
|
||||
// Initialize hasher for the leaf
|
||||
hasher[0] = Poseidon(2);
|
||||
hasher[0].inputs[0] <== leaf;
|
||||
hasher[0].inputs[1] <== salt;
|
||||
|
||||
// Hash up the Merkle tree
|
||||
for (var i = 0; i < n - 1; i++) {
|
||||
hasher[i + 1] = Poseidon(2);
|
||||
|
||||
// Choose left or right based on path index
|
||||
hasher[i + 1].inputs[0] <== pathIndices[i] * pathElements[i] + (1 - pathIndices[i]) * hasher[i].out;
|
||||
hasher[i + 1].inputs[1] <== pathIndices[i] * hasher[i].out + (1 - pathIndices[i]) * pathElements[i];
|
||||
}
|
||||
|
||||
// Ensure final hash equals root
|
||||
hasher[n - 1].out === root;
|
||||
|
||||
// Compute nullifier as hash(leaf, salt)
|
||||
component nullifierHasher = Poseidon(2);
|
||||
nullifierHasher.inputs[0] <== leaf;
|
||||
nullifierHasher.inputs[1] <== salt;
|
||||
nullifierHasher.out === nullifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bid Range Proof Circuit
|
||||
*
|
||||
* Proves that a bid is within a valid range without revealing the amount
|
||||
*/
|
||||
|
||||
template BidRangeProof() {
|
||||
// Public signals
|
||||
signal input commitment;
|
||||
signal input minAmount;
|
||||
signal input maxAmount;
|
||||
|
||||
// Private signals
|
||||
signal input bid;
|
||||
signal input salt;
|
||||
|
||||
// Component for hashing commitment
|
||||
component commitmentHasher = Poseidon(2);
|
||||
commitmentHasher.inputs[0] <== bid;
|
||||
commitmentHasher.inputs[1] <== salt;
|
||||
commitmentHasher.out === commitment;
|
||||
|
||||
// Components for range checking
|
||||
component minChecker = GreaterEqThan(8);
|
||||
component maxChecker = GreaterEqThan(8);
|
||||
|
||||
// Convert amounts to 8-bit representation
|
||||
component bidBits = Num2Bits(64);
|
||||
component minBits = Num2Bits(64);
|
||||
component maxBits = Num2Bits(64);
|
||||
|
||||
bidBits.in <== bid;
|
||||
minBits.in <== minAmount;
|
||||
maxBits.in <== maxAmount;
|
||||
|
||||
// Check bid >= minAmount
|
||||
for (var i = 0; i < 64; i++) {
|
||||
minChecker.in[i] <== bidBits.out[i] - minBits.out[i];
|
||||
}
|
||||
minChecker.out === 1;
|
||||
|
||||
// Check maxAmount >= bid
|
||||
for (var i = 0; i < 64; i++) {
|
||||
maxChecker.in[i] <== maxBits.out[i] - bidBits.out[i];
|
||||
}
|
||||
maxChecker.out === 1;
|
||||
}
|
||||
|
||||
// Main component instantiation
|
||||
component main = SimpleReceipt();
|
||||
BIN
apps/coordinator-api/src/app/zk-circuits/receipt_simple.r1cs
Normal file
BIN
apps/coordinator-api/src/app/zk-circuits/receipt_simple.r1cs
Normal file
Binary file not shown.
1172
apps/coordinator-api/src/app/zk-circuits/receipt_simple.sym
Normal file
1172
apps/coordinator-api/src/app/zk-circuits/receipt_simple.sym
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
const wc = require("./witness_calculator.js");
|
||||
const { readFileSync, writeFile } = require("fs");
|
||||
|
||||
if (process.argv.length != 5) {
|
||||
console.log("Usage: node generate_witness.js <file.wasm> <input.json> <output.wtns>");
|
||||
} else {
|
||||
const input = JSON.parse(readFileSync(process.argv[3], "utf8"));
|
||||
|
||||
const buffer = readFileSync(process.argv[2]);
|
||||
wc(buffer).then(async witnessCalculator => {
|
||||
/*
|
||||
const w= await witnessCalculator.calculateWitness(input,0);
|
||||
for (let i=0; i< w.length; i++){
|
||||
console.log(w[i]);
|
||||
}*/
|
||||
const buff= await witnessCalculator.calculateWTNSBin(input,0);
|
||||
writeFile(process.argv[4], buff, function(err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,381 @@
|
||||
module.exports = async function builder(code, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
let wasmModule;
|
||||
try {
|
||||
wasmModule = await WebAssembly.compile(code);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log("\nTry to run circom --c in order to generate c++ code instead\n");
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
let wc;
|
||||
|
||||
let errStr = "";
|
||||
let msgStr = "";
|
||||
|
||||
const instance = await WebAssembly.instantiate(wasmModule, {
|
||||
runtime: {
|
||||
exceptionHandler : function(code) {
|
||||
let err;
|
||||
if (code == 1) {
|
||||
err = "Signal not found.\n";
|
||||
} else if (code == 2) {
|
||||
err = "Too many signals set.\n";
|
||||
} else if (code == 3) {
|
||||
err = "Signal already set.\n";
|
||||
} else if (code == 4) {
|
||||
err = "Assert Failed.\n";
|
||||
} else if (code == 5) {
|
||||
err = "Not enough memory.\n";
|
||||
} else if (code == 6) {
|
||||
err = "Input signal array access exceeds the size.\n";
|
||||
} else {
|
||||
err = "Unknown error.\n";
|
||||
}
|
||||
throw new Error(err + errStr);
|
||||
},
|
||||
printErrorMessage : function() {
|
||||
errStr += getMessage() + "\n";
|
||||
// console.error(getMessage());
|
||||
},
|
||||
writeBufferMessage : function() {
|
||||
const msg = getMessage();
|
||||
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
||||
if (msg === "\n") {
|
||||
console.log(msgStr);
|
||||
msgStr = "";
|
||||
} else {
|
||||
// If we've buffered other content, put a space in between the items
|
||||
if (msgStr !== "") {
|
||||
msgStr += " "
|
||||
}
|
||||
// Then append the message to the message we are creating
|
||||
msgStr += msg;
|
||||
}
|
||||
},
|
||||
showSharedRWMemory : function() {
|
||||
printSharedRWMemory ();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const sanityCheck =
|
||||
options
|
||||
// options &&
|
||||
// (
|
||||
// options.sanityCheck ||
|
||||
// options.logGetSignal ||
|
||||
// options.logSetSignal ||
|
||||
// options.logStartComponent ||
|
||||
// options.logFinishComponent
|
||||
// );
|
||||
|
||||
|
||||
wc = new WitnessCalculator(instance, sanityCheck);
|
||||
return wc;
|
||||
|
||||
function getMessage() {
|
||||
var message = "";
|
||||
var c = instance.exports.getMessageChar();
|
||||
while ( c != 0 ) {
|
||||
message += String.fromCharCode(c);
|
||||
c = instance.exports.getMessageChar();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
function printSharedRWMemory () {
|
||||
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);
|
||||
}
|
||||
|
||||
// If we've buffered other content, put a space in between the items
|
||||
if (msgStr !== "") {
|
||||
msgStr += " "
|
||||
}
|
||||
// Then append the value to the message we are creating
|
||||
msgStr += (fromArray32(arr).toString());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class WitnessCalculator {
|
||||
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 Uint32Array(this.n32);
|
||||
for (let i=0; i<this.n32; i++) {
|
||||
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
|
||||
}
|
||||
this.prime = fromArray32(arr);
|
||||
|
||||
this.witnessSize = this.instance.exports.getWitnessSize();
|
||||
|
||||
this.sanityCheck = sanityCheck;
|
||||
}
|
||||
|
||||
circom_version() {
|
||||
return this.instance.exports.getVersion();
|
||||
}
|
||||
|
||||
async _doCalculateWitness(input_orig, sanityCheck) {
|
||||
//input is assumed to be a map from signals to arrays of bigints
|
||||
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
|
||||
let prefix = "";
|
||||
var input = new Object();
|
||||
//console.log("Input: ", input_orig);
|
||||
qualify_input(prefix,input_orig,input);
|
||||
//console.log("Input after: ",input);
|
||||
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]);
|
||||
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
||||
if (signalSize < 0){
|
||||
throw new Error(`Signal ${k} not found\n`);
|
||||
}
|
||||
if (fArr.length < signalSize) {
|
||||
throw new Error(`Not enough values for input signal ${k}\n`);
|
||||
}
|
||||
if (fArr.length > signalSize) {
|
||||
throw new Error(`Too many values for input signal ${k}\n`);
|
||||
}
|
||||
for (let i=0; i<fArr.length; i++) {
|
||||
const arrFr = toArray32(normalize(fArr[i],this.prime),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 calculateBinWitness(input, sanityCheck) {
|
||||
|
||||
const buff32 = new Uint32Array(this.witnessSize*this.n32);
|
||||
const buff = new Uint8Array( buff32.buffer);
|
||||
await this._doCalculateWitness(input, sanityCheck);
|
||||
|
||||
for (let i=0; i<this.witnessSize; i++) {
|
||||
this.instance.exports.getWitness(i);
|
||||
const pos = i*this.n32;
|
||||
for (let j=0; j<this.n32; j++) {
|
||||
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||
}
|
||||
}
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function qualify_input_list(prefix,input,input1){
|
||||
if (Array.isArray(input)) {
|
||||
for (let i = 0; i<input.length; i++) {
|
||||
let new_prefix = prefix + "[" + i + "]";
|
||||
qualify_input_list(new_prefix,input[i],input1);
|
||||
}
|
||||
} else {
|
||||
qualify_input(prefix,input,input1);
|
||||
}
|
||||
}
|
||||
|
||||
function qualify_input(prefix,input,input1) {
|
||||
if (Array.isArray(input)) {
|
||||
a = flatArray(input);
|
||||
if (a.length > 0) {
|
||||
let t = typeof a[0];
|
||||
for (let i = 1; i<a.length; i++) {
|
||||
if (typeof a[i] != t){
|
||||
throw new Error(`Types are not the same in the key ${prefix}`);
|
||||
}
|
||||
}
|
||||
if (t == "object") {
|
||||
qualify_input_list(prefix,input,input1);
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
} else if (typeof input == "object") {
|
||||
const keys = Object.keys(input);
|
||||
keys.forEach( (k) => {
|
||||
let new_prefix = prefix == ""? k : prefix + "." + k;
|
||||
qualify_input(new_prefix,input[k],input1);
|
||||
});
|
||||
} else {
|
||||
input1[prefix] = input;
|
||||
}
|
||||
}
|
||||
|
||||
function toArray32(rem,size) {
|
||||
const res = []; //new Uint32Array(size); //has no unshift
|
||||
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;
|
||||
}
|
||||
|
||||
function fromArray32(arr) { //returns a BigInt
|
||||
var res = BigInt(0);
|
||||
const radix = BigInt(0x100000000);
|
||||
for (let i = 0; i<arr.length; i++) {
|
||||
res = res*radix + BigInt(arr[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function flatArray(a) {
|
||||
var res = [];
|
||||
fillArray(res, a);
|
||||
return res;
|
||||
|
||||
function fillArray(res, a) {
|
||||
if (Array.isArray(a)) {
|
||||
for (let i=0; i<a.length; i++) {
|
||||
fillArray(res, a[i]);
|
||||
}
|
||||
} else {
|
||||
res.push(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(n, prime) {
|
||||
let res = BigInt(n) % prime
|
||||
if (res < 0) res += prime
|
||||
return res
|
||||
}
|
||||
|
||||
function fnvHash(str) {
|
||||
const uint64_max = BigInt(2) ** BigInt(64);
|
||||
let hash = BigInt("0xCBF29CE484222325");
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
hash ^= BigInt(str[i].charCodeAt());
|
||||
hash *= BigInt(0x100000001B3);
|
||||
hash %= uint64_max;
|
||||
}
|
||||
let shash = hash.toString(16);
|
||||
let n = 16 - shash.length;
|
||||
shash = '0'.repeat(n).concat(shash);
|
||||
return shash;
|
||||
}
|
||||
Reference in New Issue
Block a user