Skip to content

[WIP] add get / store preprocessing hooks functionality #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions demos/average/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Demos Template

Internal template for use when creating templates.

## File structure
Demos should consist of the following parts:
1. Server script: the defaul server script should be enough unless (1) additional extensions are applied. (2) server side computation is needed.
2. Web Based Party: Made from the following files:
..* client.html: UI for the browser.
..* client.js: Handlers for UI buttons, and input validations.
3. Node.js Based Party:
..* party.js: main entry point. Parses input from the command line and initializes the computation.
4. The MPC protocol: implemented in *mpc.js*. You should code your protocol in the compute function inside mpc.js, this file is used in both the browser
and node.js versions of the demo.
5. test.js: mocha unit tests.

## File to modify
1. client.html: change the title and different labels, as well as the UI if needed.
2. mpc.js: to encode your protocol and use any needed extensions.
3. test.js: generic test code should work for most demos, you will need to add code to generate appropriate inputs/test cases, and to compute the expected results (not in MPC) for these inputs.
4. server.js: Modify the last two lines in the template file (logs) to show the right command to run the parties.

## Running Demos
1. Running a server:
```shell
node index.js demos/<demo-name>/server.js
```

2. Either open browser based parties by going to *http://localhost:8080/demos/<demo-name>/client.html* in the browser, or a node.js party by running
```shell
node party.js <input>
```

3. Running tests: make sure a server is running and then use:
```shell
npm run-script test-demo -- demos/<demo-name>/test.js
```
30 changes: 30 additions & 0 deletions demos/average/client.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!-- Basic UI for running the demo in the browser -->

<html>
<head>
<title>Average Numbers under MPC</title>
<style>
.error {
color: #FF0000;
}
</style>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script src="/dist/jiff-client.js"></script>

<!-- Contains UI Handlers and Input Checks -->
<script type="text/javascript" src="./client.js"></script>

<!-- Contains the MPC implementation -->
<script type="text/javascript" src="./mpc.js"></script>
</head>
<body>
<h1>Connect JIFF</h1>
<label for="computation_id">Computation ID</label><input id="computation_id" value="test"></input><br/><br/>
<label for="count">Party Count<label> <input id="count" pattern="[0-9]*" value="2"> &nbsp; <button id="connectButton" onclick="connect();">Connect</button>
<br/><br/>
<hr/>
<h1>Average Numbers under MPC</h1>
<label for="number">Input Number (between 0 and 100)</label> <input id="number" pattern="[0-9]+"> &nbsp; <button onclick="submit();" disabled="disabled" id="button">Average</button><br/>
<div id="output"></div>
</body>
</html>
67 changes: 67 additions & 0 deletions demos/average/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Do not modify this file unless you have too
* This file has UI handlers.
*/
// eslint-disable-next-line no-unused-vars
function connect() {
$('#connectButton').prop('disabled', true);
var computation_id = $('#computation_id').val();
var party_count = parseInt($('#count').val());

if (isNaN(party_count)) {
$('#output').append('<p class="error">Party count must be a valid number!</p>');
$('#connectButton').prop('disabled', false);
} else {
var options = { party_count: party_count};
options.onError = function (_, error) {
$('#output').append('<p class="error">'+error+'</p>');
};
options.onConnect = function () {
$('#button').attr('disabled', false);
$('#output').append('<p>All parties Connected!</p>');
};

var hostname = window.location.hostname.trim();
var port = window.location.port;
if (port == null || port === '') {
port = '80';
}
if (!(hostname.startsWith('http://') || hostname.startsWith('https://'))) {
hostname = 'http://' + hostname;
}
if (hostname.endsWith('/')) {
hostname = hostname.substring(0, hostname.length-1);
}
if (hostname.indexOf(':') > -1 && hostname.lastIndexOf(':') > hostname.indexOf(':')) {
hostname = hostname.substring(0, hostname.lastIndexOf(':'));
}

hostname = hostname + ':' + port;

// eslint-disable-next-line no-undef
mpc.connect(hostname, computation_id, options);
}
}

// eslint-disable-next-line no-unused-vars
function submit() {
var input = parseInt($('#number').val());

if (isNaN(input)) {
$('#output').append('<p class="error">Input a valid number!</p>');
} else if (100 < input || input < 0 || input !== Math.floor(input)) {
$('#output').append('<p class="error">Input a WHOLE number between 0 and 100!</p>');
} else {
$('#button').attr('disabled', true);
$('#output').append('<p>Starting...</p>');

// eslint-disable-next-line no-undef
var promise = mpc.compute(input);
promise.then(handleResult);
}
}

function handleResult(result) {
$('#output').append('<p>Result is: ' + result + '</p>');
$('#button').attr('disabled', false);
}
42 changes: 42 additions & 0 deletions demos/average/mpc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(function (exports, node) {


/**
* Connect to the server and initialize the jiff instance
*/
exports.connect = function (hostname, computation_id, options) {
var opt = Object.assign({}, options);
if (node) {
// eslint-disable-next-line no-undef
JIFFClient = require('../../lib/jiff-client');
}

// Added options goes here
opt.crypto_provider = false;



// eslint-disable-next-line no-undef
saved_instance = new JIFFClient(hostname, computation_id, opt);
// if you need any extensions, put them here
return saved_instance;
};

/**
* The MPC computation
*/
exports.compute = function (input, jiff_instance) {
if (jiff_instance == null) {
jiff_instance = saved_instance;
}
var shares = jiff_instance.share(input);
var average = shares[1];
for (var i = 2; i <= jiff_instance.party_count; i++) {
average = average.sadd(shares[i]);
}
// var oneOverN = Number.parseFloat((1/jiff_instance.party_count).toFixed(2)); // convert 1/n to fixed point number
var result = average.cdiv(jiff_instance.party_count);
// Return a promise to the final output(s)
return jiff_instance.open(result);
};
}((typeof exports === 'undefined' ? this.mpc = {} : exports), typeof exports !== 'undefined'));
48 changes: 48 additions & 0 deletions demos/average/party.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Do not change this unless you have to.
* This code parses input command line arguments,
* and calls the appropriate initialization and MPC protocol from ./mpc.js
*/

console.log('Command line arguments: <input> [<party count> [<computation_id> [<party id>]]]]');

var mpc = require('./mpc');

// Read Command line arguments
var input = parseInt(process.argv[2], 10);

var party_count = process.argv[3];
if (party_count == null) {
party_count = 2;
} else {
party_count = parseInt(party_count);
}

var computation_id = process.argv[4];
if (computation_id == null) {
computation_id = 'test';
}

var party_id = process.argv[5];
if (party_id != null) {
party_id = parseInt(party_id, 10);
}

// JIFF options
var options = {party_count: party_count, party_id: party_id};
options.onConnect = function (jiff_instance) {
jiff_instance.preprocessing('cdiv', 1);
jiff_instance.preprocessing('open', 1);
jiff_instance.executePreprocessing(function() {
var promise = mpc.compute(input);
promise.then(function (v) {
console.log(v);
// drop mongoose share collections here
jiff_instance.disconnect(false, true);
});
});
};


// Connect
mpc.connect('http://localhost:8080', computation_id, options);
16 changes: 16 additions & 0 deletions demos/average/preprocess.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var mongoose = require('mongoose');


var shareSchema = mongoose.Schema({
op_id: String,
ready: Boolean,
value: String,
holders: Array,
threshold: Number,
Zp: Number,
partyID: Number,
onDemand: Boolean
})


module.exports = mongoose.model("Share", shareSchema);
19 changes: 19 additions & 0 deletions demos/average/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var path = require('path');
var express = require('express');
var app = express();
var http = require('http').Server(app);
var JIFFServer = require('../../lib/jiff-server');
new JIFFServer(http, { logs:true });


// Serve static files.
app.use('/demos', express.static(path.join(__dirname, '..', '..', 'demos')));
app.use('/dist', express.static(path.join(__dirname, '..', '..', 'dist')));
app.use('/lib/ext', express.static(path.join(__dirname, '..', '..', 'lib', 'ext')));
http.listen(8080, function () {
console.log('listening on *:8080');
});

console.log('Direct your browser to http://localhost:8080/demos/average/client.html.');
console.log('To run a server-based party: node demos/average/party <input');
console.log();
124 changes: 124 additions & 0 deletions demos/average/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Chai
var assert = require('chai').assert;

var mpc = require('./mpc.js');
var showProgress = true;

// Generic Testing Parameters
var party_count = 3;
var parallelismDegree = 5; // Max number of test cases running in parallel
var n = 10; // Number of test cases in total
var Zp = null;

// Parameters specific to this demo
/* PUT PARAMETERS HERE */

/**
* CHANGE THIS: Generate inputs for your tests
* Should return an object with this format:
* {
* 'party_id': [ 'test1_input', 'test2_input', ...]
* }
*/
function generateInputs(party_count) {
var inputs = {};

// Generate test cases one at a time
for (var t = 0; t < n; t++) {
/*
* INPUT GENERATION CODE GOES HERE
*/
}

return inputs;
}

/**
* CHANGE THIS: Compute the expected results not in MPC
* @param {object} inputs - same format as generateInputs output.
* Should return a single array with the expected result for every test in order
* [ 'test1_output', 'test2_output', ... ]
*/
function computeResults(inputs) {
var results = [];

for (var j = 0; j < n; j++) {
/*
* COMPUTING THE RESULT IN THE OPEN CODE GOES HERE
*/
}
return results;
}

/**
* Do not change unless you have to.
*/
// eslint-disable-next-line no-undef
describe('Test', function () {
this.timeout(0); // Remove timeout

// eslint-disable-next-line no-undef
it('Exhaustive', function (done) {
var count = 0;

var inputs = generateInputs(party_count);
var realResults = computeResults(inputs);

var onConnect = function (jiff_instance) {
var partyInputs = inputs[jiff_instance.id];

var testResults = [];
(function one_test_case(j) {
if (jiff_instance.id === 1 && showProgress) {
console.log('\tStart ', j > partyInputs.length ? partyInputs.length : j, '/', partyInputs.length);
}

if (j < partyInputs.length) {
var promises = [];
for (var t = 0; t < parallelismDegree && (j + t) < partyInputs.length; t++) {
promises.push(mpc.compute(partyInputs[j + t], jiff_instance));
}

Promise.all(promises).then(function (parallelResults) {
for (var t = 0; t < parallelResults.length; t++) {
testResults.push(parallelResults[t]);
}

one_test_case(j+parallelismDegree);
});

return;
}

// If we reached here, it means we are done
count++;
for (var i = 0; i < testResults.length; i++) {
// construct debugging message
var ithInputs = inputs[1][i] + '';
for (var p = 2; p <= party_count; p++) {
ithInputs += ',' + inputs[p][i];
}
var msg = 'Party: ' + jiff_instance.id + '. inputs: [' + ithInputs + ']';

// assert results are accurate
try {
assert.deepEqual(testResults[i].toString(), realResults[i].toString(), msg);
} catch (assertionError) {
done(assertionError);
done = function () { };
}
}

jiff_instance.disconnect(true);
if (count === party_count) {
done();
}
})(0);
};

var options = { party_count: party_count, onError: console.log, onConnect: onConnect, Zp: Zp, crypto_provider: true };
for (var i = 0; i < party_count; i++) {
mpc.connect('http://localhost:8080', 'mocha-test', options);
}
});
});
Loading