snarkjs/src/calculateWitness.js
src/calculateWitness.jsRaw
1/*
2 Copyright 2018 0kims association.
3
4 This file is part of zksnark JavaScript library.
5
6 zksnark JavaScript library is a free software: you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as published by the
8 Free Software Foundation, either version 3 of the License, or (at your option)
9 any later version.
10
11 zksnark JavaScript library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along with
17 zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20const bigInt = require("./bigint");
21
22module.exports = calculateWitness;
23
24function calculateWitness(circuit, inputSignals, log) {
25 log = log || (() => {});
26 const ctx = new RTCtx(circuit, log);
27
28 function iterateSelector(values, sels, cb) {
29 if (!Array.isArray(values)) {
30 return cb(sels, values);
31 }
32 for (let i=0; i<values.length; i++) {
33 sels.push(i);
34 iterateSelector(values[i], sels, cb);
35 sels.pop(i);
36 }
37 }
38
39 ctx.setSignal("one", [], bigInt(1));
40
41 for (let c in ctx.notInitSignals) {
42 if (ctx.notInitSignals[c] == 0) ctx.triggerComponent(c);
43 }
44
45 for (let s in inputSignals) {
46 ctx.currentComponent = "main";
47 iterateSelector(inputSignals[s], [], function(selector, value) {
48 ctx.setSignal(s, selector, bigInt(value));
49 });
50 }
51
52 for (let i=0; i<circuit.nInputs; i++) {
53 const idx = circuit.inputIdx(i);
54 if (typeof(ctx.witness[idx]) == "undefined") {
55 throw new Error("Input Signal not assigned: " + circuit.signalNames(i));
56 }
57 }
58
59
60 for (let i=0; i<ctx.witness.length; i++) {
61 if (typeof(ctx.witness[i]) == "undefined") {
62 throw new Error("Signal not assigned: " + circuit.signalNames(i));
63 }
64 log(circuit.signalNames(i) + " --> " + ctx.witness[i].toString());
65 }
66 return ctx.witness.slice(0, circuit.nVars);
67}
68
69class RTCtx {
70 constructor(circuit, log) {
71 this.log = log || function() {};
72 this.scopes = [];
73 this.circuit = circuit;
74 this.witness = new Array(circuit.nSignals);
75 this.notInitSignals = {};
76 for (let c in this.circuit.components) {
77 this.notInitSignals[c] = this.circuit.components[c].inputSignals;
78 }
79 }
80
81 _sels2str(sels) {
82 let res = "";
83 for (let i=0; i<sels.length; i++) {
84 res += `[${sels[i]}]`;
85 }
86 return res;
87 }
88
89 setPin(componentName, componentSels, signalName, signalSels, value) {
90 let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
91 fullName += this._sels2str(componentSels) +
92 "."+
93 signalName+
94 this._sels2str(signalSels);
95 this.setSignalFullName(fullName, value);
96 }
97
98 setSignal(name, sels, value) {
99 let fullName = this.currentComponent ? this.currentComponent + "." + name : name;
100 fullName += this._sels2str(sels);
101 this.setSignalFullName(fullName, value);
102 }
103
104 triggerComponent(c) {
105 this.log("Component Treiggered: " + c);
106
107 // Set notInitSignals to -1 to not initialize again
108 this.notInitSignals[c] --;
109 const oldComponent = this.currentComponent;
110 this.currentComponent = this.circuit.components[c].name;
111 const template = this.circuit.components[c].template;
112
113 const newScope = {};
114 for (let p in this.circuit.components[c].params) {
115 newScope[p] = this.circuit.components[c].params[p];
116 }
117
118 const oldScope = this.scopes;
119 this.scopes = [ this.scopes[0], newScope ];
120
121 // TODO set params.
122
123 this.circuit.templates[template](this);
124 this.scopes = oldScope;
125 this.currentComponent = oldComponent;
126 }
127
128 callFunction(functionName, params) {
129
130 const newScope = {};
131 for (let p=0; p<this.circuit.functions[functionName].params.length; p++) {
132 const paramName = this.circuit.functions[functionName].params[p];
133 newScope[paramName] = params[p];
134 }
135
136 const oldScope = this.scopes;
137 this.scopes = [ this.scopes[0], newScope ];
138
139 // TODO set params.
140
141 const res = this.circuit.functions[functionName].func(this);
142 this.scopes = oldScope;
143
144 return res;
145 }
146
147 setSignalFullName(fullName, value) {
148 this.log("set " + fullName + " <-- " + value.toString());
149 const sId = this.circuit.getSignalIdx(fullName);
150 let firstInit =false;
151 if (typeof(this.witness[sId]) == "undefined") {
152 firstInit = true;
153 }
154 this.witness[sId] = value;
155 const callComponents = [];
156 for (let i=0; i<this.circuit.signals[sId].triggerComponents.length; i++) {
157 var idCmp = this.circuit.signals[sId].triggerComponents[i];
158 if (firstInit) this.notInitSignals[idCmp] --;
159 callComponents.push(idCmp);
160 }
161 callComponents.map( (c) => {
162 if (this.notInitSignals[c] == 0) this.triggerComponent(c);
163 });
164 return value;
165 }
166
167 setVar(name, sels, value) {
168 function setVarArray(a, sels2, value) {
169 if (sels2.length == 1) {
170 a[sels2[0]] = value;
171 } else {
172 if (typeof(a[sels2[0]]) == "undefined") a[sels2[0]] = [];
173 setVarArray(a[sels2[0]], sels2.slice(1), value);
174 }
175 }
176 const scope = this.scopes[this.scopes.length-1];
177 if (sels.length == 0) {
178 scope[name] = value;
179 } else {
180 if (typeof(scope[name]) == "undefined") scope[name] = [];
181 setVarArray(scope[name], sels, value);
182 }
183 return value;
184 }
185
186 getVar(name, sels) {
187 function select(a, sels2) {
188 return (sels2.length == 0) ? a : select(a[sels2[0]], sels2.slice(1));
189 }
190 for (let i=this.scopes.length-1; i>=0; i--) {
191 if (typeof(this.scopes[i][name]) != "undefined") return select(this.scopes[i][name], sels);
192 }
193 throw new Error("Variable not defined: " + name);
194 }
195
196 getSignal(name, sels) {
197 let fullName = name=="one" ? "one" : this.currentComponent + "." + name;
198 fullName += this._sels2str(sels);
199 return this.getSignalFullName(fullName);
200 }
201
202
203 getPin(componentName, componentSels, signalName, signalSels) {
204 let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
205 fullName += this._sels2str(componentSels) +
206 "."+
207 signalName+
208 this._sels2str(signalSels);
209 return this.getSignalFullName(fullName);
210 }
211
212 getSignalFullName(fullName) {
213 const sId = this.circuit.getSignalIdx(fullName);
214 if (typeof(this.witness[sId]) == "undefined") {
215 throw new Error("Signal not initialized: "+fullName);
216 }
217 this.log("get --->" + fullName + " = " + this.witness[sId].toString() );
218 return this.witness[sId];
219 }
220
221 assert(a,b) {
222 const ba = bigInt(a);
223 const bb = bigInt(b);
224 if (!ba.equals(bb)) {
225 throw new Error("Constraint doesn't match: " + ba.toString() + " != " + bb.toString());
226 }
227 }
228}