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:
oib
2026-02-27 21:46:54 +01:00
parent a477681c4b
commit 864ef4343e
152 changed files with 45716 additions and 94 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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;
});
});
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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]

View File

@@ -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;
});
});
}

View File

@@ -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;
}

View File

@@ -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);

View 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();

View 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();

File diff suppressed because it is too large Load Diff

View File

@@ -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;
});
});
}

View File

@@ -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;
}