circom/src/calculateWitness.js
src/calculateWitness.jsRaw
1const bigInt = require("big-integer");
2
3module.exports = calculateWitness;
4
5function calculateWitness(circuit, inputSignals) {
6 const ctx = new RTCtx(circuit);
7
8 function iterateSelector(values, sels, cb) {
9 if (!Array.isArray(values)) {
10 return cb(sels, values);
11 }
12 for (let i=0; i<values.length; i++) {
13 sels.push(i);
14 iterateSelector(values[i], sels, cb);
15 sels.pop(i);
16 }
17 }
18
19 ctx.setSignal("one", [], bigInt(1));
20
21 for (let c in ctx.notInitSignals) {
22 if (ctx.notInitSignals[c] == 0) ctx.triggerComponent(c);
23 }
24
25 for (let s in inputSignals) {
26 ctx.currentComponent = "main";
27 iterateSelector(inputSignals[s], [], function(selector, value) {
28 ctx.setSignal(s, selector, bigInt(value));
29 });
30 }
31
32 for (let i=0; i<ctx.witness.length; i++) {
33 if (typeof(ctx.witness[i]) == "undefined") {
34 throw("Signal not assigned: " + ctx.circuit.witnessNames[i].join(", "));
35 }
36 console.log(ctx.circuit.witnessNames[i].join(",") + " --> " + ctx.witness[i].toString());
37 }
38 return ctx.witness;
39}
40
41class RTCtx {
42 constructor(circuit) {
43 this.scopes = [];
44 this.circuit = circuit;
45 this.witness = [];
46 this.notInitSignals = {};
47 for (let c in this.circuit.components) {
48 this.notInitSignals[c] = this.circuit.components[c].inputSignals;
49 if (this.notInitSignals == 0) {
50 this.currentComponent = c;
51 this.components.calc(this);
52 this.currentComponent = null;
53 }
54 }
55 }
56
57 _sels2str(sels) {
58 let res = "";
59 for (let i=0; i<sels.length; i++) {
60 res += `[${sels[i]}]`;
61 }
62 return res;
63 }
64
65 setPin(componentName, componentSels, signalName, signalSels, value) {
66 let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
67 fullName += this._sels2str(componentSels) +
68 "."+
69 signalName+
70 this._sels2str(signalSels);
71 this.setSignalFullName(fullName, value);
72 }
73
74 setSignal(name, sels, value) {
75 let fullName = this.currentComponent ? this.currentComponent + "." + name : name;
76 fullName += this._sels2str(sels);
77 this.setSignalFullName(fullName, value);
78 }
79
80 triggerComponent(c) {
81 console.log("Component Treiggered: " + c);
82
83 // Set notInitSignals to -1 to not initialize again
84 this.notInitSignals[c] --;
85 const oldComponent = this.currentComponent;
86 this.currentComponent = c;
87 const template = this.circuit.components[c].template;
88
89 const newScope = {};
90 for (let p in this.circuit.components[c].params) {
91 newScope[p] = this.circuit.components[c].params[p];
92 }
93
94 const oldScope = this.scopes;
95 this.scopes = [ this.scopes[0], newScope ];
96
97 // TODO set params.
98
99 this.circuit.templates[template](this);
100 this.scopes = oldScope;
101 this.currentComponent = oldComponent;
102 }
103
104 callFunction(functionName, params) {
105
106 const newScope = {};
107 for (let p=0; p<this.circuit.functionParams[functionName].length; p++) {
108 const paramName = this.circuit.functionParams[functionName][p];
109 newScope[paramName] = params[p];
110 }
111
112 const oldScope = this.scopes;
113 this.scopes = [ this.scopes[0], newScope ];
114
115 // TODO set params.
116
117 const res = this.circuit.functions[functionName](this);
118 this.scopes = oldScope;
119
120 return res;
121 }
122
123 setSignalFullName(fullName, value) {
124 console.log("set " + fullName + " <-- " + value.toString());
125 const sId = this.circuit.signals[fullName].id;
126 let firstInit =false;
127 if (!this.witness[sId]) {
128 firstInit = true;
129 }
130 this.witness[sId] = value;
131 const callComponents = [];
132 for (let i=0; i<this.circuit.witnessNames[sId].length; i++) {
133 const ss = this.circuit.witnessNames[sId][i];
134 if (this.circuit.signals[ss].direction == "IN") {
135 if (firstInit) this.notInitSignals[ this.circuit.signals[ss].component] --;
136 callComponents.push(this.circuit.signals[ss].component);
137 }
138 }
139 for (let i in callComponents) {
140 const c= callComponents[i];
141 if (this.notInitSignals[c] == 0) this.triggerComponent(c);
142 }
143 return value;
144 }
145
146 setVar(name, sels, value) {
147 function setVarArray(a, sels2, value) {
148 if (sels2.length == 1) {
149 a[sels2[0]] = value;
150 } else {
151 if (typeof(a[sels2[0]]) == "undefined") a[sels2[0]] = [];
152 setVarArray(a[sels2[0]], sels2.slice(1), value);
153 }
154 }
155 const scope = this.scopes[this.scopes.length-1];
156 if (sels.length == 0) {
157 scope[name] = value;
158 } else {
159 if (typeof(scope[name]) == "undefined") scope[name] = [];
160 setVarArray(scope[name], sels, value);
161 }
162 return value;
163 }
164
165 getVar(name, sels) {
166 function select(a, sels2) {
167 return (sels2.length == 0) ? a : select(a[sels2[0]], sels2.slice(1));
168 }
169 for (let i=this.scopes.length-1; i>=0; i--) {
170 if (typeof(this.scopes[i][name]) != "undefined") return select(this.scopes[i][name], sels);
171 }
172 throw new Error("Variable not defined: " + name);
173 }
174
175 getSignal(name, sels) {
176 let fullName = name=="one" ? "one" : this.currentComponent + "." + name;
177 fullName += this._sels2str(sels);
178 return this.getSignalFullName(fullName);
179 }
180
181
182 getPin(componentName, componentSels, signalName, signalSels) {
183 let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
184 fullName += this._sels2str(componentSels) +
185 "."+
186 signalName+
187 this._sels2str(signalSels);
188 return this.getSignalFullName(fullName);
189 }
190
191 getSignalFullName(fullName) {
192 const sId = this.circuit.signals[fullName].id;
193 if (typeof(this.witness[sId]) == "undefined") {
194 throw new Error("Signal not initialized: "+fullName);
195 }
196 console.log("get --->" + fullName + " = " + this.witness[sId].toString() );
197 return this.witness[sId];
198 }
199
200 assert(a,b) {
201 const ba = bigInt(a);
202 const bb = bigInt(b);
203 if (!ba.equals(bb)) {
204 throw new Error("Constrain doesn't match: " + ba.toString() + " != " + bb.toString());
205 }
206 }
207
208
209}