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