arnaucube
arnaucube/snarkjsPublic Self-hosted repository
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("Constraint doesn't match: " + ba.toString() + " != " + bb.toString());
225        }
226    }
227}