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