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 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}