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