snarkjs/src/calculateWitness.js
src/calculateWitness.jsRaw
1/*
2 Copyright 2018 0kims association.
3
4 This file is part of snarkjs.
5
6 snarkjs 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 snarkjs 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 snarkjs. 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 return ctx.witness;
68}
69
70class RTCtx {
71 constructor(circuit, log) {
72 this.log = log || function() {};
73 this.scopes = [];
74 this.circuit = circuit;
75 this.witness = new Array(circuit.nSignals);
76 this.notInitSignals = {};
77 for (let c in this.circuit.components) {
78 this.notInitSignals[c] = this.circuit.components[c].inputSignals;
79 }
80 }
81
82 _sels2str(sels) {
83 let res = "";
84 for (let i=0; i<sels.length; i++) {
85 res += `[${sels[i]}]`;
86 }
87 return res;
88 }
89
90 setPin(componentName, componentSels, signalName, signalSels, value) {
91 let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
92 fullName += this._sels2str(componentSels) +
93 "."+
94 signalName+
95 this._sels2str(signalSels);
96 this.setSignalFullName(fullName, value);
97 }
98
99 setSignal(name, sels, value) {
100 let fullName = this.currentComponent ? this.currentComponent + "." + name : name;
101 fullName += this._sels2str(sels);
102 this.setSignalFullName(fullName, value);
103 }
104
105 triggerComponent(c) {
106 this.log("Component Treiggered: " + this.circuit.components[c].name);
107// console.log("Start Component Treiggered: " + this.circuit.components[c].name);
108
109 // Set notInitSignals to -1 to not initialize again
110 this.notInitSignals[c] --;
111 const oldComponent = this.currentComponent;
112 this.currentComponent = this.circuit.components[c].name;
113 const template = this.circuit.components[c].template;
114
115 const newScope = {};
116 for (let p in this.circuit.components[c].params) {
117 newScope[p] = this.circuit.components[c].params[p];
118 }
119
120 const oldScope = this.scopes;
121 this.scopes = [ this.scopes[0], newScope ];
122
123 // TODO set params.
124
125 this.circuit.templates[template](this);
126 this.scopes = oldScope;
127 this.currentComponent = oldComponent;
128// console.log("End Component Treiggered: " + this.circuit.components[c].name);
129 }
130
131 callFunction(functionName, params) {
132
133 const newScope = {};
134 for (let p=0; p<this.circuit.functions[functionName].params.length; p++) {
135 const paramName = this.circuit.functions[functionName].params[p];
136 newScope[paramName] = params[p];
137 }
138
139 const oldScope = this.scopes;
140 this.scopes = [ this.scopes[0], newScope ];
141
142 // TODO set params.
143
144 const res = this.circuit.functions[functionName].func(this);
145 this.scopes = oldScope;
146
147 return res;
148 }
149
150 setSignalFullName(fullName, value) {
151 this.log("set " + fullName + " <-- " + value.toString());
152 const sId = this.circuit.getSignalIdx(fullName);
153 let firstInit =false;
154 if (typeof(this.witness[sId]) == "undefined") {
155 firstInit = true;
156 }
157 this.witness[sId] = bigInt(value);
158 const callComponents = [];
159 for (let i=0; i<this.circuit.signals[sId].triggerComponents.length; i++) {
160 var idCmp = this.circuit.signals[sId].triggerComponents[i];
161 if (firstInit) this.notInitSignals[idCmp] --;
162 callComponents.push(idCmp);
163 }
164 callComponents.map( (c) => {
165 if (this.notInitSignals[c] == 0) this.triggerComponent(c);
166 });
167 return this.witness[sId];
168 }
169
170 setVar(name, sels, value) {
171 function setVarArray(a, sels2, value) {
172 if (sels2.length == 1) {
173 a[sels2[0]] = value;
174 } else {
175 if (typeof(a[sels2[0]]) == "undefined") a[sels2[0]] = [];
176 setVarArray(a[sels2[0]], sels2.slice(1), value);
177 }
178 }
179 const scope = this.scopes[this.scopes.length-1];
180 if (sels.length == 0) {
181 scope[name] = value;
182 } else {
183 if (typeof(scope[name]) == "undefined") scope[name] = [];
184 setVarArray(scope[name], sels, value);
185 }
186 return value;
187 }
188
189 getVar(name, sels) {
190 function select(a, sels2) {
191 return (sels2.length == 0) ? a : select(a[sels2[0]], sels2.slice(1));
192 }
193 for (let i=this.scopes.length-1; i>=0; i--) {
194 if (typeof(this.scopes[i][name]) != "undefined") return select(this.scopes[i][name], sels);
195 }
196 throw new Error("Variable not defined: " + name);
197 }
198
199 getSignal(name, sels) {
200 let fullName = name=="one" ? "one" : this.currentComponent + "." + name;
201 fullName += this._sels2str(sels);
202 return this.getSignalFullName(fullName);
203 }
204
205
206 getPin(componentName, componentSels, signalName, signalSels) {
207 let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
208 fullName += this._sels2str(componentSels) +
209 "."+
210 signalName+
211 this._sels2str(signalSels);
212 return this.getSignalFullName(fullName);
213 }
214
215 getSignalFullName(fullName) {
216 const sId = this.circuit.getSignalIdx(fullName);
217 if (typeof(this.witness[sId]) == "undefined") {
218 throw new Error("Signal not initialized: "+fullName);
219 }
220 this.log("get --->" + fullName + " = " + this.witness[sId].toString() );
221 return this.witness[sId];
222 }
223
224 assert(a,b) {
225 const ba = bigInt(a);
226 const bb = bigInt(b);
227 if (!ba.equals(bb)) {
228 throw new Error("Constraint doesn't match: " + ba.toString() + " != " + bb.toString());
229 }
230 }
231}