Skip to content

Commit e30bc8a

Browse files
committed
Fix unit tests; add CLI options (multiple inputs, specific circuit).
1 parent e1c5b42 commit e30bc8a

File tree

6 files changed

+131
-61
lines changed

6 files changed

+131
-61
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ Unit tests of functional components (single-process without sockets) can be run
9595
```shell
9696
mocha test
9797
```
98+
It is possible to restrict the end-to-end unit tests to only test inputs on a specific circuit (note that the `.txt` circuit file extension is omitted).
99+
```shell
100+
mocha test --circuit logic-and-4-bit
101+
```
102+
The number of distinct inputs on which to run the circuits being tested can be specified.
103+
```shell
104+
mocha test --trials 3
105+
```
106+
The two options can also be combined.
107+
```shell
108+
mocha test --circuit logic-and-8-bit --trials 10
109+
```
98110

99111
### End-to-end Tests
100112
All of the built-in test vectors can be verified in `npm test` or, equivalently, `node test/suite/simulate.js`. Communications between the server, garbler and evaluator are automated. You do not need to already have a server running; tests are run over port 3001.

src/comm/channel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ ChannelSimulated.prototype.sendOblivious = function (pair) {
111111
};
112112

113113
/**
114-
* Receive the specified value under the specified key.
114+
* Receive the bit-specified value from first pair in queue.
115115
* @param {number} bit - Bit to determine which value to receive
116116
* @returns {string} Next available value
117117
*/

src/data/assignment.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ function Assignment() {
2121
/**
2222
* Assign an ordered collection of labels to a wire index.
2323
* @param {number} index - Index of wire to which to assign labels
24-
* @param {Object[]} labels - Array of one or two labels
24+
* @param {Object[]} labels - Ordered collection of labels
2525
*/
2626
Assignment.prototype.set = function (index, labels) {
2727
this.mapping[index] = labels;
2828
};
2929

3030
/**
3131
* Get the labels at the specified wire index.
32-
* @param {number} index - Index of wire for which to return the label
33-
* @param {Object[]} labels - Array of one or two labels
32+
* @param {number} index - Index of wire for which to return labels
33+
* @returns {Object[]} Ordered collection of labels
3434
*/
3535
Assignment.prototype.get = function (index) {
3636
return this.mapping[index];
@@ -52,7 +52,7 @@ Assignment.prototype.toJSON = function () {
5252
};
5353

5454
/**
55-
* Return the data structure instance as a JSON string.
55+
* Turn an assignment instance into a JSON string.
5656
* @returns {string} Data structure as a JSON string
5757
*/
5858
Assignment.prototype.toJSONString = function () {
@@ -61,6 +61,7 @@ Assignment.prototype.toJSONString = function () {
6161

6262
/**
6363
* Build a data structure instance from its JSON representation.
64+
* @param {string} json - Assignment instance as a JSON object
6465
* @returns {Object} Instance of the data structure
6566
*/
6667
Assignment.prototype.fromJSON = function (json) {
@@ -73,6 +74,7 @@ Assignment.prototype.fromJSON = function (json) {
7374

7475
/**
7576
* Build a data structure instance from its JSON string representation.
77+
* @param {string} json - Assignment instance in JSON string form
7678
* @returns {Object} Instance of the data structure
7779
*/
7880
Assignment.prototype.fromJSONString = function (s) {
@@ -82,7 +84,7 @@ Assignment.prototype.fromJSONString = function (s) {
8284
/**
8385
* Return a subset of the map corresponding to the supplied indices.
8486
* @param {number[]} indices - Indices of map entries to keep in result
85-
* @returns {Object} Data structure as a JSON object
87+
* @returns {Object} New assignment data structure instance
8688
*/
8789
Assignment.prototype.copyWithOnlyIndices = function (indices) {
8890
var assignment = new Assignment();

src/evaluate.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ const crypto = require('./util/crypto');
1919
*/
2020
function receiveMessages(channel, circuit, input) {
2121
const inputPair = (new Array(1 + input.length)).concat(input);
22+
23+
// Receive the list of garbled gates.
2224
var messages = [channel.receiveDirect('gatesGarbled')];
2325

2426
// Receive each of the garbler's input labels.
2527
for (var i = 0; i < circuit.wire_in_count/2; i++) {
2628
messages.push(channel.receiveDirect('wire[' + circuit.wire_in_index[i] + ']'));
2729
}
2830

29-
// Promises to each of the evaluator's input labels.
31+
// Receive each of the evaluator's input labels using the input bits
32+
// available to the evaluator.
3033
for (var i = circuit.wire_in_count/2; i < circuit.wire_in_count; i++) {
3134
messages.push(channel.receiveOblivious(inputPair[circuit.wire_in_index[i]]));
3235
}
@@ -71,8 +74,23 @@ function evaluateGate(gate_id, gate, gateGarbled, wireToLabels) {
7174
}
7275
}
7376

77+
/**
78+
* Evaluate all the gates (stateless version).
79+
* @param {Object} circuit - Circuit in which to garble the gates
80+
* @param {Object} gatesGarbled - Ordered collection of garbled gates
81+
* @param {Object} wireToLabels - Labeled wire data structure
82+
* @returns {Object} Mapping from each wire index to two labels
83+
*/
84+
function evaluateGates(circuit, gatesGarbled, wireToLabels) {
85+
for (var i = 0; i < circuit.gate_count; i++) {
86+
this.evaluateGate(i, circuit.gate[i], gatesGarbled.get(i), wireToLabels);
87+
}
88+
return wireToLabels;
89+
}
90+
7491
module.exports = {
7592
receiveMessages: receiveMessages,
7693
processMessages: processMessages,
7794
evaluateGate: evaluateGate,
95+
evaluateGates: evaluateGates
7896
};

src/garble.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,24 @@ function outputLabelsToBits(circuit, wireToLabels, outputWireToLabels) {
159159
return output;
160160
}
161161

162+
/**
163+
* Garble all the gates (stateless version).
164+
* @param {Object} circuit - Circuit in which to garble the gates
165+
* @param {Object} wireToLabels - Mapping from each wire index to two labels
166+
* @returns {Object} Ordered collection of garbled gates
167+
*/
168+
function garbleGates(circuit, wireToLabels) {
169+
var gatesGarbled = new gate.GatesGarbled();
170+
for (var i = 0; i < circuit.gate_count; i++) {
171+
gatesGarbled.set(i, garbleGate(i, circuit.gate[i], wireToLabels));
172+
}
173+
return gatesGarbled;
174+
}
175+
162176
module.exports = {
163177
generateWireToLabelsMap: generateWireToLabelsMap,
164178
garbleGate: garbleGate,
179+
garbleGates: garbleGates,
165180
sendInputWireToLabelsMap: sendInputWireToLabelsMap,
166181
outputLabelsToBits: outputLabelsToBits
167182
};

test/test.js

Lines changed: 77 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ var add32_json = {
431431
* @returns {number[]} Computed bit vector output
432432
*/
433433
function protocolPureEndToEnd(circuit, inputs, chan, a, gg) {
434+
434435
// Split inputs into two halves (to be divided
435436
// between garbler and evaluator agents).
436437
var bs = [];
@@ -529,61 +530,83 @@ describe('end-to-end', function() {
529530
'compare-eq-zero-64-bit': function (a) { return a.orBits().not(); }
530531
}
531532

533+
// Check for command-line arguments.
534+
var trials = 1;
535+
var filename_only = "*";
536+
if (process.argv.length === 5 || process.argv.length === 7) {
537+
for (var i = 3; i <= 5; i += 2)
538+
if (process.argv[i] === '--trials')
539+
trials = parseInt(process.argv[i+1]);
540+
for (var i = 3; i <= 5; i += 2)
541+
if (process.argv[i] === '--circuit')
542+
filename_only = process.argv[i+1];
543+
console.log(filename_only);
544+
}
545+
532546
// Test each circuit.
533547
for (let i = 0; i < filenames.length; i++) {
534-
it(filenames[i], async function() {
535-
this.timeout(4*60*1000); // Necessary for larger circuits.
536-
537-
// Create the simulated communications channel.
538-
var chan = new channel.ChannelSimulated();
539-
540-
// Load circuit file and perform end-to-end test.
541-
let raw = await fs.readFile('./circuits/bristol/' + filenames[i] + '.txt', 'utf8');
542-
let c = circuit.fromBristolFashion(raw);
543-
544-
var inputs = [];
545-
for (var j = 0; j < c.value_in_count; j++) {
546-
inputs.push(bits.random(c.value_in_length[j], i+j+1));
547-
}
548-
let outEval = c.evaluate(inputs);
549-
550-
// Load precomputed data if it is available.
551-
let a = null, gg = null;
552-
try {
553-
let aString = await fs.readFile('./circuits/gg/' + filenames[i] + '.assignment.json', 'utf8');
554-
let ggString = await fs.readFile('./circuits/gg/' + filenames[i] + '.gg.json', 'utf8');
555-
a = assignment.Assignment.prototype.fromJSONString(aString);
556-
gg = gate.GatesGarbled.prototype.fromJSONString(ggString);
557-
} catch (err) {}
558-
559-
let outEtoE = protocolPureEndToEnd(c, inputs, chan, a, gg);
560-
561-
// Confirm that the circuit is mathematically correct if a
562-
// reference function for the circuit is provided.
563-
if (filenames[i] in functions) {
564-
let outRef = functions[filenames[i]](...inputs);
565-
expect(outEval.toString()).to.eql(outRef.toString());
566-
}
567-
568-
// Do the evaluation and end-to-end protocol output bit vectors match?
569-
expect(outEval.toString()).to.eql(outEtoE.toString());
570548

571-
// Confirm that communicated messages conform to schemas.
572-
let gatesGarbledSchemaString = await fs.readFile('./schemas/gates.garbled.schema.json', 'utf8');
573-
let gatesGarbledSchema = JSON.parse(gatesGarbledSchemaString);
574-
expect(JSON.parse(chan.direct['gatesGarbled'])).to.be.jsonSchema(gatesGarbledSchema);
575-
576-
let assignmentSchemaString = await fs.readFile('./schemas/assignment.schema.json', 'utf8');
577-
let assignmentSchema = JSON.parse(assignmentSchemaString);
578-
expect(JSON.parse(chan.direct['outputWireToLabels'])).to.be.jsonSchema(assignmentSchema);
579-
580-
let labelSchemaString = await fs.readFile('./schemas/label.schema.json', 'utf8');
581-
let labelSchema = JSON.parse(labelSchemaString);
582-
for (var key in chan.direct) {
583-
if (key.startsWith("wire[")) {
584-
expect(JSON.parse(chan.direct[key])).to.be.jsonSchema(labelSchema);
585-
}
586-
}
587-
});
588-
}
549+
// Only test the specified circuit if a user specified one.
550+
console.log(filenames[i], filename_only === filenames[i]);
551+
if (filename_only === "*" || filename_only === filenames[i]) {
552+
553+
// Test the circuit on each input.
554+
for (let trial = 0; trial < trials; trial++) {
555+
it(filenames[i], async function() {
556+
this.timeout(4*60*1000); // Necessary for larger circuits.
557+
558+
// Create the simulated communications channel.
559+
var chan = new channel.ChannelSimulated();
560+
561+
// Load circuit file and perform end-to-end test.
562+
let raw = await fs.readFile('./circuits/bristol/' + filenames[i] + '.txt', 'utf8');
563+
let c = circuit.fromBristolFashion(raw);
564+
565+
var inputs = [];
566+
for (var j = 0; j < c.value_in_count; j++) {
567+
inputs.push(bits.random(c.value_in_length[j], 1+trial+j));
568+
}
569+
let outEval = c.evaluate(inputs);
570+
571+
// Load precomputed data if it is available.
572+
let a = null, gg = null;
573+
try {
574+
let aString = await fs.readFile('./circuits/gg/' + filenames[i] + '.assignment.json', 'utf8');
575+
let ggString = await fs.readFile('./circuits/gg/' + filenames[i] + '.gg.json', 'utf8');
576+
a = assignment.Assignment.prototype.fromJSONString(aString);
577+
gg = gate.GatesGarbled.prototype.fromJSONString(ggString);
578+
} catch (err) {}
579+
580+
let outEtoE = protocolPureEndToEnd(c, inputs, chan, a, gg);
581+
582+
// Confirm that the circuit is mathematically correct if a
583+
// reference function for the circuit is provided.
584+
if (filenames[i] in functions) {
585+
let outRef = functions[filenames[i]](...inputs);
586+
expect(outEval.toString()).to.eql(outRef.toString());
587+
}
588+
589+
// Do the evaluation and end-to-end protocol output bit vectors match?
590+
expect(outEval.toString()).to.eql(outEtoE.toString());
591+
592+
// Confirm that communicated messages conform to schemas.
593+
let gatesGarbledSchemaString = await fs.readFile('./schemas/gates.garbled.schema.json', 'utf8');
594+
let gatesGarbledSchema = JSON.parse(gatesGarbledSchemaString);
595+
expect(JSON.parse(chan.direct['gatesGarbled'])).to.be.jsonSchema(gatesGarbledSchema);
596+
597+
let assignmentSchemaString = await fs.readFile('./schemas/assignment.schema.json', 'utf8');
598+
let assignmentSchema = JSON.parse(assignmentSchemaString);
599+
expect(JSON.parse(chan.direct['outputWireToLabels'])).to.be.jsonSchema(assignmentSchema);
600+
601+
let labelSchemaString = await fs.readFile('./schemas/label.schema.json', 'utf8');
602+
let labelSchema = JSON.parse(labelSchemaString);
603+
for (var key in chan.direct) {
604+
if (key.startsWith("wire[")) {
605+
expect(JSON.parse(chan.direct[key])).to.be.jsonSchema(labelSchema);
606+
}
607+
}
608+
}); // it()
609+
} // for each trial
610+
} // if a specified circuit
611+
} // for each circuit
589612
});

0 commit comments

Comments
 (0)