XOR API (beta) and Examples
The purpose of this document is to show you how to interact programmatically with the xor.
A basic understanding of XOR and it's components is strongly recommendet before starting with this documenation.
The API is based on socket.io's implementaion of websockets. To install socket.io
, run the following command in your project:
npm i -s socket.io-client
once you installed the socket.io
dependency, add it to your project:
const io = require('socket.io-client');
To initiate a socket with socket.io
you first need to establish a connection with the server:
const xorBaseUrl = 'https://<xor-portal url>';
const xorSocket = io(`${xorBaseUrl}/analyst`, { forceNew: true });
Now that you have a socket, you can send events:
// list available datasets
xorSocket.emit('listPrivateDatasets', {}, answer => console.log(answer));
To receive an event, you need to define a listener:
// listen for offline phase completion and log how long it took
xorSocket.on('offlinePhaseCompleted', answer => console.log(`offline: ${answer.timings}ms`));
When creating a new computation, the algorithm parameters are sent to xor when calling createNewComputation
. The structure of the algorithm
object is the following.
{
"netconfigId":"local-all",
"ucid":"4e7e2f65-701b-89a7-274a-b30c27c06fdd",
"algorithm":{
"type": "LINREG",
"compileParams":{
"audit":false
},
"sources":{
"a":{
"stype":"private",
"name":"radar_0",
"rows":50000,
"cols":9,
"ownerId":0
},
"b":{
"stype":"private",
"name":"radar_1",
"rows":10000,
"cols":9,
"ownerId":1
}
},
"input":{
"X":"a(1-8);b(1-8)",
"y":"a(9);b(9)",
"intercept":true
}
}
}
Field | Possible Values | Description |
---|---|---|
algorithm.type |
string | function that shall be run on the data |
compileParams.audit |
boolean | an audit archive containing all triplets, shares, network traces will be collected and created during the computation. this feature is not available in a production environment! |
sources[id].stype |
private, public, secretshared | type of dataset. private means the data resides in the private data folder of the xor machine. public means a publicly known value. secretshared means a dataset uploaded to the portal and secret shared with the xor machines |
sources[id].name |
string | name of the dataset without file extension |
sources[id].rows |
integer | total number of rows in the dataset |
sources[id].cols |
integer | total number of columns in the dataset |
sources[id].ownerId |
integer | player id of the xor machine |
algorithm.input.X |
string | training data composition |
algorithm.input.y |
string | label / dependent variable composition |
algorithm.input.model |
string | model composition |
algorithm.input.intercept |
boolean | should an intercept be fitted if applicable |
The available algorithms are the following (more details):
algorithm.type | Description | mandatory compileParams |
---|---|---|
MATRIXCOLUMNSUM | column-wise sum | |
CORRELATION | column-wise correlation | |
MEAN | column-wise mean | |
VARIANCE | column-wise variance | |
LINREG | linear regression | |
LOGREG | logistic regression | irlsIters (iterations) |
BATCHLOGREG | (alpha) logistic regression using batch gradient descent for large datasets | batchSize , epochs |
REGRESSIONPREDICT | dot product between thetas and test matrix |
|
RSS | residual sum of squares | |
PSI | private set intersection | psiBound |
The composition of the input (algorithm.input) follows the following syntax:
# counting starts with 1, not with 0
# column selection
a(1-5) # take columns 1 to 5 of dataset a
a(1-5,8,10) # take columns 1 to 5, 8 and 10 from dataset a
# row selection
a[1-100] # take rows 1 to 100 from dataset a
# horizontal stacking
a:b # horizontally stack columns of datasets a and b
a(1-3):b(5) # stack columns 1-3 of dataset a with column 5 of dataset b
# vertical stacking
a;b # vertically stack all rows of datasets a and b
a[1-100];b[200-300] # vertically stack rows 1-100 of dataset a with rows 200-300 from b
# mixed stacking / selection
a[1-10](3,4):b[1-10](1,2);c[1-10](1-4)
When an issued command fails, due to bad input or state, the callback will return an error. E.g:
// listPrivateDatasets with bad netconfigId
xorPortalSocket.emit('listPrivateDatasets', {netconfigId: 'bad'}, console.log)
// Outputs: {error: "requested netconfig is not ready"}
So typically, error handling can be implemented like this:
xorPortalSocket.emit('listPrivateDatasets', {netconfigId: 'bad'}, (answer) => {
if (answer && answer.error) {
console.error(answer.error);
// handle error
} else {
// handle success
}
});
You can also send data streams across web sockets. xor uses the socket.io-stream
package package to accomplish that.
npm install --save socket.io-stream
Example usage:
// require socket.io-stream package
const stream = ss.createStream();
const ss = require('socket.io-stream');
// send a file across a stream, logs answer
ss(xorPortalSocket).emit('preUploadAnalystData', stream, { ucid, 'mydataset' }, console.log);
const blobStream = ss.createBlobReadStream(file);
blobStream.pipe(stream);
Event Name | Query JSON | Callback Answer JSON | Description |
---|---|---|---|
createAndJoinNewUcid | { netconfigId } |
{ ucid } |
joins a given netconfig and receives a new computation id ucid |
joinExistingUcid | { ucid } |
{ state } |
joins an existing ucid and retrieves the state for this computation |
leaveUcid | { ucid } |
{} |
leaves an existing ucid |
Event Name | Query JSON | Callback Answer JSON | Description |
---|---|---|---|
listAvailableNetconfigs | {} |
{netconfig1, netconfig2, ...} |
lists all available netconfigs. Each netconfig object contains a numplayers and netconfigId field |
listPrivateDatasets | { netconfigId } |
{answer: [ds1, ds2, ...]} |
lists all available private datasets. Each dataset ds object contains an ownerId and name field |
listAnalystDatasets | { ucid } |
{answer: [ds1, ds2, ...]} |
lists all available datasets uploaded by the analyst. Each ds object is a string containing the name of the dataset |
listHeaders | { netconfigId, name, ownerId } |
{ name, ownerId, rows, cols, headers} |
retrieves information about a specific dataset. The headers field is an array containing the header for each column |
listAnalystHeaders | { ucid, name } |
{ name, rows, cols, headers } |
retrieves information about a specific dataset uploaded by the analyst. The headers field is an array containing the header for each column |
Event Name | Query JSON | Callback Answer JSON | Description |
---|---|---|---|
preUploadAnalystData | { ucid, sourcename } , |
{} |
uploads a dataset to the xor portal through a stream |
Event Name | Query JSON | Callback Answer JSON | Description |
---|---|---|---|
createNewComputation | { netconfigId, ucid, algorithm } |
{} |
creates and validate new computation based on provided algorithm parameters |
initiateCompilePhase | { ucid } |
{} |
starts the computation created in createNewComputation step |
When a command fails during processing, the xor portal will return an error event that should be catched by the corresponding on
listener. E.g:
// valid query, but failure will occur while the xor machines or service encounters an error
xorPortalSocket.emit('createNewComputation', query, console.error); // no error logged here!
xorSocket.on('newComputationCreated', (answer) => {
if (answer && answer.error) {
// error will be caught on the corresponding completion listener
console.error(answer.error)
// handle error
} else {
// handle success
}
});
Event Name | Answer JSON | Description |
---|---|---|
createNewComputationProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
uploadPhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
compilePhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
offlinePhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
preprocessingPhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
onlinePhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
postprocessingPhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
resultPhaseProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
assembleAuditArchiveProgress | { progress } | returns a progress value between 0 and 1 (0% - 100%) for this stage |
Each completion listener will receive the ucid
in order to distinguish multiple parallel computations, as well as timings
containing timing information in milli seconds.
Event Name | Answer JSON | Description |
---|---|---|
newComputationCreated | { ucid, timings } |
event received when createNewComputation completed |
compilePhaseCompleted | { ucid, timings } |
event received when circuit compilation completed |
uploadPhaseCompleted | { ucid, timings } |
event received when secret shares upload completed |
offlinePhaseCompleted | { ucid, timings } |
event received when offline phase completed |
preprocessingPhaseCompleted | { ucid, timings } |
event received when preprocessing completed |
onlinePhaseCompleted | { ucid, timings } |
event received when online phase completed |
postprocessingPhaseCompleted | { ucid, timings } |
event received when post processing completed |
resultPhaseCompleted | { ucid, result, timings } |
event received when results available. results contains a preview of the result |
auditPhaseCompleted | { ucid, timings } |
event received when audit archive available |
Event Name | Answer JSON | Description |
---|---|---|
xorMachineDisconnected | { netconfigId, playerId } |
received when xor machine disconnected |
xorMachineConnected | { netconfigId, playerId } |
received when xor machine connected |
xorServiceDisconnected | { netconfigId } |
received when xor service disconnected |
xorServiceConnected | { netconfigId } |
received when xor service connected |
A result preview is sent along the resultPhaseCompleted
event. The full result shall be downloaded through and HTTP request:
const https = require('https');
https.get(`${xorBaseUrl}/result/${ucid}`, (resp) => {
let data = '';
// A chunk of data has been received.
resp.on('data', (chunk) => data += chunk);
// The whole response has been received. Print out the result.
resp.on('end', () => {
// do something with data
});
}).on("error", console.error);
the same can be done with the audit archive
A sample implementation using the apis is provided in this repo. It uses these algorithm parameters.
To get access to a demo environment contact us.