mirror of
https://github.com/privacy-scaling-explorations/emp-wasm.git
synced 2026-01-09 10:07:54 -05:00
Merge pull request #15 from privacy-scaling-explorations/improve-buffering-performance
Improve buffering performance
This commit is contained in:
1012
package-lock.json
generated
1012
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "emp-wasm",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"description": "Wasm build of authenticated garbling from [emp-toolkit/emp-ag2pc](https://github.com/emp-toolkit/emp-ag2pc).",
|
||||
"type": "module",
|
||||
"main": "dist/src/ts/index.js",
|
||||
@@ -23,7 +23,7 @@
|
||||
"peerjs": "^1.5.4",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.4.8",
|
||||
"vite": "^6.3.5",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
|
||||
#include "emp-tool/io/i_raw_io.h"
|
||||
#include "emp-ag2pc/2pc.h"
|
||||
@@ -24,45 +25,121 @@ EM_JS(void, send_js, (int to_party, char channel_label, const void* data, size_t
|
||||
});
|
||||
|
||||
// Implement recv_js function to receive data from JavaScript to C++
|
||||
EM_ASYNC_JS(void, recv_js, (int from_party, char channel_label, void* data, size_t len), {
|
||||
EM_ASYNC_JS(size_t, recv_js, (int from_party, char channel_label, void* data, size_t min_len, size_t max_len), {
|
||||
if (!Module.emp?.io?.recv) {
|
||||
reject(new Error("Module.emp.io.recv is not defined in JavaScript."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for data from JavaScript
|
||||
const dataArray = await Module.emp.io.recv(from_party - 1, String.fromCharCode(channel_label), len);
|
||||
const dataArray = await Module.emp.io.recv(from_party - 1, String.fromCharCode(channel_label), min_len, max_len);
|
||||
|
||||
// Copy data from JavaScript Uint8Array to WebAssembly memory
|
||||
HEAPU8.set(dataArray, data);
|
||||
|
||||
// Return the length of the received data
|
||||
return dataArray.length;
|
||||
});
|
||||
|
||||
class RawIOJS;
|
||||
std::map<int, RawIOJS*> raw_io_map;
|
||||
int next_raw_io_id = 0;
|
||||
size_t MAX_SEND_BUFFER_SIZE = 64 * 1024;
|
||||
|
||||
void actual_flush_all();
|
||||
|
||||
class RawIOJS : public IRawIO {
|
||||
public:
|
||||
int other_party;
|
||||
char channel_label;
|
||||
std::vector<uint8_t> send_buffer; // TODO: Max buffer size?
|
||||
std::vector<uint8_t> recv_buffer;
|
||||
size_t recv_start = 0;
|
||||
size_t recv_end = 0;
|
||||
int id;
|
||||
|
||||
RawIOJS(
|
||||
int other_party,
|
||||
char channel_label
|
||||
):
|
||||
other_party(other_party),
|
||||
channel_label(channel_label)
|
||||
{}
|
||||
channel_label(channel_label),
|
||||
recv_buffer(64 * 1024)
|
||||
{
|
||||
id = next_raw_io_id++;
|
||||
raw_io_map[id] = this;
|
||||
}
|
||||
|
||||
~RawIOJS() {
|
||||
raw_io_map.erase(id);
|
||||
}
|
||||
|
||||
void send(const void* data, size_t len) override {
|
||||
send_js(other_party, channel_label, data, len);
|
||||
if (send_buffer.size() + len > MAX_SEND_BUFFER_SIZE) {
|
||||
actual_flush();
|
||||
}
|
||||
|
||||
// This will still exceed max size if len > MAX_SEND_BUFFER_SIZE, that's ok
|
||||
send_buffer.resize(send_buffer.size() + len);
|
||||
|
||||
std::memcpy(send_buffer.data() + send_buffer.size() - len, data, len);
|
||||
}
|
||||
|
||||
void recv(void* data, size_t len) override {
|
||||
recv_js(other_party, channel_label, data, len);
|
||||
if (recv_start + len > recv_end) {
|
||||
if (recv_start + len > recv_buffer.size()) {
|
||||
// copy within
|
||||
size_t recv_len = recv_end - recv_start;
|
||||
std::memmove(recv_buffer.data(), recv_buffer.data() + recv_start, recv_len);
|
||||
recv_start = 0;
|
||||
recv_end = recv_len;
|
||||
}
|
||||
|
||||
size_t bytes_needed = recv_start + len - recv_end;
|
||||
size_t room = recv_buffer.size() - recv_end;
|
||||
|
||||
if (bytes_needed > room) {
|
||||
size_t size = recv_buffer.size();
|
||||
|
||||
while (bytes_needed > room) {
|
||||
size *= 2;
|
||||
room = size - recv_end;
|
||||
}
|
||||
|
||||
recv_buffer.resize(size);
|
||||
}
|
||||
|
||||
actual_flush_all();
|
||||
size_t bytes_received = recv_js(other_party, channel_label, recv_buffer.data() + recv_end, bytes_needed, room);
|
||||
recv_end += bytes_received;
|
||||
|
||||
if (bytes_received < bytes_needed) {
|
||||
throw std::runtime_error("recv failed");
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(data, recv_buffer.data() + recv_start, len);
|
||||
recv_start += len;
|
||||
}
|
||||
|
||||
void flush() override {
|
||||
// Ignored for now
|
||||
// ignored for now
|
||||
}
|
||||
|
||||
void actual_flush() {
|
||||
if (send_buffer.size() > 0) {
|
||||
send_js(other_party, channel_label, send_buffer.data(), send_buffer.size());
|
||||
send_buffer.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void actual_flush_all() {
|
||||
for (auto& [key, raw_io] : raw_io_map) {
|
||||
raw_io->actual_flush();
|
||||
}
|
||||
}
|
||||
|
||||
class MultiIOJS : public IMultiIO {
|
||||
public:
|
||||
int mParty;
|
||||
@@ -284,6 +361,9 @@ void run_2pc_impl(int party, int nP) {
|
||||
twopc.function_dependent();
|
||||
|
||||
std::vector<bool> output_bits = twopc.online(input_bits, true);
|
||||
|
||||
actual_flush_all();
|
||||
|
||||
handle_output_bits(output_bits);
|
||||
} catch (const std::exception& e) {
|
||||
handle_error(e.what());
|
||||
@@ -339,6 +419,8 @@ void run_mpc_impl(int party, int nP) {
|
||||
output_bits.push_back(output.get_plaintext_bit(i));
|
||||
}
|
||||
|
||||
actual_flush_all();
|
||||
|
||||
handle_output_bits(output_bits);
|
||||
} catch (const std::exception& e) {
|
||||
handle_error(e.what());
|
||||
|
||||
@@ -5,7 +5,7 @@ export default class BufferQueue {
|
||||
private buffer: Uint8Array;
|
||||
private bufferStart: number;
|
||||
private bufferEnd: number;
|
||||
private pendingPops: number[];
|
||||
private pendingPops: { min_len: number, max_len: number }[];
|
||||
private pendingPopsResolvers: {
|
||||
resolve: ((value: Uint8Array) => void),
|
||||
reject: (e: Error) => void,
|
||||
@@ -65,19 +65,21 @@ export default class BufferQueue {
|
||||
* @param len - The number of bytes to pop from the buffer.
|
||||
* @returns A promise resolving with the popped data as a Uint8Array.
|
||||
*/
|
||||
pop(len: number): Promise<Uint8Array> {
|
||||
if (typeof len !== 'number' || len < 0) {
|
||||
return Promise.reject(new Error('Length must be non-negative integer'));
|
||||
pop(min_len: number, max_len: number): Promise<Uint8Array> {
|
||||
if (typeof min_len !== 'number' || typeof max_len !== 'number' || min_len < 0 || max_len < 0 || min_len > max_len) {
|
||||
return Promise.reject(new Error('Invalid min/max lengths'));
|
||||
}
|
||||
|
||||
if (this.bufferEnd - this.bufferStart >= len) {
|
||||
const result = this.buffer.slice(this.bufferStart, this.bufferStart + len);
|
||||
this.bufferStart += len;
|
||||
if (this.bufferEnd - this.bufferStart >= min_len) {
|
||||
const available_len = this.bufferEnd - this.bufferStart;
|
||||
const provide_len = Math.min(max_len, available_len);
|
||||
const result = this.buffer.slice(this.bufferStart, this.bufferStart + provide_len);
|
||||
this.bufferStart += result.length;
|
||||
this._compactBuffer();
|
||||
return Promise.resolve(result);
|
||||
} else if (!this.closed) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pendingPops.push(len);
|
||||
this.pendingPops.push({ min_len, max_len });
|
||||
this.pendingPopsResolvers.push({ resolve, reject });
|
||||
});
|
||||
} else {
|
||||
@@ -109,10 +111,12 @@ export default class BufferQueue {
|
||||
*/
|
||||
private _resolvePendingPops(): void {
|
||||
while (this.pendingPops.length > 0) {
|
||||
const len = this.pendingPops[0];
|
||||
if (this.bufferEnd - this.bufferStart >= len) {
|
||||
const data = this.buffer.slice(this.bufferStart, this.bufferStart + len);
|
||||
this.bufferStart += len;
|
||||
const { min_len, max_len } = this.pendingPops[0];
|
||||
if (this.bufferEnd - this.bufferStart >= min_len) {
|
||||
const available_len = this.bufferEnd - this.bufferStart;
|
||||
const provide_len = Math.min(max_len, available_len);
|
||||
const data = this.buffer.slice(this.bufferStart, this.bufferStart + provide_len);
|
||||
this.bufferStart += data.length;
|
||||
this.pendingPops.shift();
|
||||
const { resolve } = this.pendingPopsResolvers.shift()!;
|
||||
resolve(data);
|
||||
|
||||
@@ -27,9 +27,9 @@ export default class BufferedIO
|
||||
this.closeOther?.();
|
||||
}
|
||||
|
||||
async recv(fromParty: number, channel: 'a' | 'b', len: number): Promise<Uint8Array> {
|
||||
async recv(fromParty: number, channel: 'a' | 'b', min_len: number, max_len: number): Promise<Uint8Array> {
|
||||
assert(fromParty === this.otherParty, 'fromParty !== this.otherParty');
|
||||
return await this.bq[channel].pop(len);
|
||||
return await this.bq[channel].pop(min_len, max_len);
|
||||
}
|
||||
|
||||
accept(channel: 'a' | 'b', data: Uint8Array) {
|
||||
|
||||
@@ -61,15 +61,25 @@ async function secureMPC({
|
||||
emp.circuit = circuit;
|
||||
emp.inputBits = inputBits;
|
||||
emp.inputBitsPerParty = inputBitsPerParty;
|
||||
emp.io = io;
|
||||
|
||||
let reject: undefined | ((error: unknown) => void) = undefined;
|
||||
const callbackRejector = new Promise((_resolve, rej) => {
|
||||
reject = rej;
|
||||
});
|
||||
reject = reject!;
|
||||
|
||||
emp.io = {
|
||||
send: useRejector(io.send.bind(io), reject),
|
||||
recv: useRejector(io.recv.bind(io), reject),
|
||||
};
|
||||
|
||||
const method = calculateMethod(mode, size, circuit);
|
||||
|
||||
const result = new Promise<Uint8Array>((resolve, reject) => {
|
||||
const result = new Promise<Uint8Array>(async (resolve, reject) => {
|
||||
try {
|
||||
emp.handleOutput = resolve;
|
||||
emp.handleError = reject;
|
||||
|
||||
callbackRejector.catch(reject);
|
||||
module[method](party, size);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
@@ -97,7 +107,12 @@ function calculateMethod(
|
||||
case 'mpc':
|
||||
return '_run_mpc';
|
||||
case 'auto':
|
||||
return size === 2 ? '_run_2pc' : '_run_mpc';
|
||||
// Advantage of 2PC specialization is small and contains "FEQ error" bug
|
||||
// for the large circuits, so the performance currently cannot be realized
|
||||
// where it matters.
|
||||
// Therefore, we default to the general N-party mpc mode, even when there
|
||||
// are only 2 parties.
|
||||
return '_run_mpc';
|
||||
|
||||
default:
|
||||
const _never: never = mode;
|
||||
@@ -125,11 +140,11 @@ onmessage = async (event) => {
|
||||
send: (toParty, channel, data) => {
|
||||
postMessage({ type: 'io_send', toParty, channel, data });
|
||||
},
|
||||
recv: (fromParty, channel, len) => {
|
||||
recv: (fromParty, channel, min_len, max_len) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = requestId++;
|
||||
pendingRequests[id] = { resolve, reject };
|
||||
postMessage({ type: 'io_recv', fromParty, channel, len, id });
|
||||
postMessage({ type: 'io_recv', fromParty, channel, min_len, max_len, id });
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -147,7 +162,7 @@ onmessage = async (event) => {
|
||||
|
||||
postMessage({ type: 'result', result });
|
||||
} catch (error) {
|
||||
postMessage({ type: 'error', error: (error as Error).stack });
|
||||
postMessage({ type: 'error', error: (error as Error).message });
|
||||
}
|
||||
} else if (message.type === 'io_recv_response') {
|
||||
const { id, data } = message;
|
||||
@@ -163,3 +178,17 @@ onmessage = async (event) => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function useRejector<F extends (...args: any[]) => any>(
|
||||
fn: F,
|
||||
reject: (error: unknown) => void,
|
||||
): F {
|
||||
return ((...args: Parameters<F>) => {
|
||||
try {
|
||||
return fn(...args);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
throw error;
|
||||
}
|
||||
}) as F;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ windowAny.internalDemo = async function(
|
||||
inputBitsPerParty: [32, 32],
|
||||
io: {
|
||||
send: (toParty, channel, data) => bobBq[channel].push(data),
|
||||
recv: (fromParty, channel, len) => aliceBq[channel].pop(len),
|
||||
recv: (fromParty, channel, min_len, max_len) => aliceBq[channel].pop(min_len, max_len),
|
||||
},
|
||||
mode,
|
||||
}),
|
||||
@@ -40,7 +40,7 @@ windowAny.internalDemo = async function(
|
||||
inputBitsPerParty: [32, 32],
|
||||
io: {
|
||||
send: (toParty, channel, data) => aliceBq[channel].push(data),
|
||||
recv: (fromParty, channel, len) => bobBq[channel].pop(len),
|
||||
recv: (fromParty, channel, min_len, max_len) => bobBq[channel].pop(min_len, max_len),
|
||||
},
|
||||
mode,
|
||||
}),
|
||||
@@ -69,7 +69,7 @@ windowAny.internalDemo3 = async function(
|
||||
inputBitsPerParty: [32, 32, 32],
|
||||
io: {
|
||||
send: (toParty, channel, data) => bqs.get(party, toParty, channel).push(data),
|
||||
recv: (fromParty, channel, len) => bqs.get(fromParty, party, channel).pop(len),
|
||||
recv: (fromParty, channel, min_len, max_len) => bqs.get(fromParty, party, channel).pop(min_len, max_len),
|
||||
},
|
||||
mode,
|
||||
})));
|
||||
@@ -342,9 +342,9 @@ function makeCopyPasteIO(otherParty: number): IO {
|
||||
|
||||
return {
|
||||
send: makeConsoleSend(otherParty),
|
||||
recv: (fromParty, channel, len) => {
|
||||
recv: (fromParty, channel, min_len, max_len) => {
|
||||
assert(fromParty === otherParty, 'Unexpected party');
|
||||
return bq[channel].pop(len);
|
||||
return bq[channel].pop(min_len, max_len);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,7 +42,17 @@ export default async function nodeSecureMPC({
|
||||
emp.circuit = circuit;
|
||||
emp.inputBits = inputBits;
|
||||
emp.inputBitsPerParty = inputBitsPerParty;
|
||||
emp.io = io;
|
||||
|
||||
let reject: undefined | ((error: unknown) => void) = undefined;
|
||||
const callbackRejector = new Promise((_resolve, rej) => {
|
||||
reject = rej;
|
||||
});
|
||||
reject = reject!;
|
||||
|
||||
emp.io = {
|
||||
send: useRejector(io.send.bind(io), reject),
|
||||
recv: useRejector(io.recv.bind(io), reject),
|
||||
};
|
||||
|
||||
const method = calculateMethod(mode, size, circuit);
|
||||
|
||||
@@ -50,6 +60,7 @@ export default async function nodeSecureMPC({
|
||||
try {
|
||||
emp.handleOutput = resolve;
|
||||
emp.handleError = reject;
|
||||
callbackRejector.catch(reject);
|
||||
|
||||
module[method](party, size);
|
||||
} catch (error) {
|
||||
@@ -74,10 +85,29 @@ function calculateMethod(
|
||||
case 'mpc':
|
||||
return '_run_mpc';
|
||||
case 'auto':
|
||||
return size === 2 ? '_run_2pc' : '_run_mpc';
|
||||
// Advantage of 2PC specialization is small and contains "FEQ error" bug
|
||||
// for the large circuits, so the performance currently cannot be realized
|
||||
// where it matters.
|
||||
// Therefore, we default to the general N-party mpc mode, even when there
|
||||
// are only 2 parties.
|
||||
return '_run_mpc';
|
||||
|
||||
default:
|
||||
const _never: never = mode;
|
||||
throw new Error('Unexpected mode: ' + mode);
|
||||
}
|
||||
}
|
||||
|
||||
function useRejector<F extends (...args: any[]) => any>(
|
||||
fn: F,
|
||||
reject: (error: unknown) => void,
|
||||
): F {
|
||||
return ((...args: Parameters<F>) => {
|
||||
try {
|
||||
return fn(...args);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
throw error;
|
||||
}
|
||||
}) as F;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ export default function secureMPC({
|
||||
const { toParty, channel, data } = message;
|
||||
io.send(toParty, channel, data);
|
||||
} else if (message.type === 'io_recv') {
|
||||
const { fromParty, channel, len } = message;
|
||||
const { fromParty, channel, min_len, max_len } = message;
|
||||
// Handle the recv request from the worker
|
||||
try {
|
||||
const data = await io.recv(fromParty, channel, len);
|
||||
const data = await io.recv(fromParty, channel, min_len, max_len);
|
||||
worker.postMessage({ type: 'io_recv_response', id: message.id, data });
|
||||
} catch (error) {
|
||||
worker.postMessage({
|
||||
@@ -80,6 +80,10 @@ export default function secureMPC({
|
||||
} else if (message.type === 'error') {
|
||||
// Reject the promise if an error occurred
|
||||
reject(new Error(message.error));
|
||||
} else if (message.type === 'log') {
|
||||
console.log('Worker log:', message.msg);
|
||||
} else {
|
||||
console.error('Unexpected message from worker:', message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type IO = {
|
||||
send: (toParty: number, channel: 'a' | 'b', data: Uint8Array) => void;
|
||||
recv: (fromParty: number, channel: 'a' | 'b', len: number) => Promise<Uint8Array>;
|
||||
recv: (fromParty: number, channel: 'a' | 'b', min_len: number, max_len: number) => Promise<Uint8Array>;
|
||||
on?: (event: 'error', listener: (error: Error) => void) => void;
|
||||
off?: (event: 'error', listener: (error: Error) => void) => void;
|
||||
close?: () => void;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import fs from 'fs/promises';
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { BufferQueue, secureMPC } from "../src/ts"
|
||||
import { BufferQueue, secureMPC } from "../src/ts";
|
||||
|
||||
describe('Secure MPC', () => {
|
||||
it('3 + 5 == 8 (2pc)', async function () {
|
||||
@@ -20,6 +22,36 @@ describe('Secure MPC', () => {
|
||||
this.timeout(20_000);
|
||||
expect(await internalDemoN(3, 5, 5)).to.deep.equal([8, 8, 8, 8, 8]);
|
||||
});
|
||||
|
||||
it('sha1("") == "da..09" (2 parties)', async function () {
|
||||
this.timeout(60_000);
|
||||
|
||||
const outputs = await internalDemoSha1N(2);
|
||||
|
||||
for (const output of outputs) {
|
||||
expect(output).to.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||
}
|
||||
});
|
||||
|
||||
it('sha1("") == "da..09" (3 parties)', async function () {
|
||||
this.timeout(60_000);
|
||||
|
||||
const outputs = await internalDemoSha1N(3);
|
||||
|
||||
for (const output of outputs) {
|
||||
expect(output).to.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||
}
|
||||
});
|
||||
|
||||
it('sha1("") == "da..09" (4 parties)', async function () {
|
||||
this.timeout(60_000);
|
||||
|
||||
const outputs = await internalDemoSha1N(4);
|
||||
|
||||
for (const output of outputs) {
|
||||
expect(output).to.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
class BufferQueueStore {
|
||||
@@ -42,6 +74,7 @@ async function internalDemo(
|
||||
mode: '2pc' | 'mpc' | 'auto' = 'auto',
|
||||
): Promise<{ alice: number, bob: number }> {
|
||||
const bqs = new BufferQueueStore();
|
||||
const add32BitCircuit = await getCircuit('adder_32bit.txt');
|
||||
|
||||
const [aliceBits, bobBits] = await Promise.all([
|
||||
secureMPC({
|
||||
@@ -55,9 +88,9 @@ async function internalDemo(
|
||||
expect(toParty).to.equal(1);
|
||||
bqs.get('alice', 'bob', channel).push(data);
|
||||
},
|
||||
recv: async (fromParty, channel, len) => {
|
||||
recv: async (fromParty, channel, min_len, max_len) => {
|
||||
expect(fromParty).to.equal(1);
|
||||
return bqs.get('bob', 'alice', channel).pop(len);
|
||||
return bqs.get('bob', 'alice', channel).pop(min_len, max_len);
|
||||
},
|
||||
},
|
||||
mode,
|
||||
@@ -73,9 +106,9 @@ async function internalDemo(
|
||||
expect(toParty).to.equal(0);
|
||||
bqs.get('bob', 'alice', channel).push(data);
|
||||
},
|
||||
recv: async (fromParty, channel, len) => {
|
||||
recv: async (fromParty, channel, min_len, max_len) => {
|
||||
expect(fromParty).to.equal(0);
|
||||
return bqs.get('alice', 'bob', channel).pop(len);
|
||||
return bqs.get('alice', 'bob', channel).pop(min_len, max_len);
|
||||
},
|
||||
},
|
||||
mode,
|
||||
@@ -94,6 +127,7 @@ async function internalDemoN(
|
||||
size: number
|
||||
): Promise<number[]> {
|
||||
const bqs = new BufferQueueStore();
|
||||
const add32BitCircuit = await getCircuit('adder_32bit.txt');
|
||||
|
||||
const inputBitsPerParty = new Array(size).fill(0);
|
||||
inputBitsPerParty[0] = 32;
|
||||
@@ -119,8 +153,8 @@ async function internalDemoN(
|
||||
send: (toParty, channel, data) => {
|
||||
bqs.get(party, toParty, channel).push(data);
|
||||
},
|
||||
recv: async (fromParty, channel, len) => {
|
||||
return bqs.get(fromParty, party, channel).pop(len);
|
||||
recv: async (fromParty, channel, min_len, max_len) => {
|
||||
return bqs.get(fromParty, party, channel).pop(min_len, max_len);
|
||||
},
|
||||
}
|
||||
})));
|
||||
@@ -160,382 +194,67 @@ function numberFrom32Bits(arr: Uint8Array): number {
|
||||
return result;
|
||||
}
|
||||
|
||||
const add32BitCircuit = `375 439
|
||||
32 32 33
|
||||
async function internalDemoSha1N(
|
||||
size: number,
|
||||
): Promise<string[]> {
|
||||
const bqs = new BufferQueueStore();
|
||||
const sha1Circuit = await getCircuit('sha-1.txt');
|
||||
|
||||
2 1 0 32 406 XOR
|
||||
2 1 5 37 373 AND
|
||||
2 1 4 36 336 AND
|
||||
2 1 10 42 340 AND
|
||||
2 1 14 46 366 AND
|
||||
2 1 24 56 341 AND
|
||||
2 1 8 40 342 AND
|
||||
2 1 1 33 343 AND
|
||||
2 1 7 39 348 AND
|
||||
2 1 28 60 349 AND
|
||||
2 1 19 51 350 AND
|
||||
2 1 2 34 351 AND
|
||||
2 1 30 62 364 AND
|
||||
2 1 13 45 352 AND
|
||||
2 1 18 50 353 AND
|
||||
2 1 11 43 355 AND
|
||||
2 1 3 35 356 AND
|
||||
2 1 16 48 359 AND
|
||||
2 1 31 63 357 AND
|
||||
2 1 27 59 358 AND
|
||||
2 1 15 47 360 AND
|
||||
2 1 17 49 361 AND
|
||||
2 1 9 41 363 AND
|
||||
2 1 32 0 278 AND
|
||||
2 1 29 61 362 AND
|
||||
2 1 6 38 365 AND
|
||||
2 1 25 57 354 AND
|
||||
2 1 20 52 367 AND
|
||||
2 1 22 54 331 AND
|
||||
2 1 21 53 371 AND
|
||||
2 1 12 44 372 AND
|
||||
2 1 23 55 339 AND
|
||||
2 1 26 58 368 AND
|
||||
1 1 56 398 INV
|
||||
1 1 3 314 INV
|
||||
1 1 40 346 INV
|
||||
1 1 62 378 INV
|
||||
1 1 6 389 INV
|
||||
1 1 28 401 INV
|
||||
1 1 10 377 INV
|
||||
1 1 13 391 INV
|
||||
1 1 27 335 INV
|
||||
1 1 7 387 INV
|
||||
1 1 24 399 INV
|
||||
1 1 54 327 INV
|
||||
1 1 36 315 INV
|
||||
1 1 52 332 INV
|
||||
1 1 50 380 INV
|
||||
1 1 57 404 INV
|
||||
1 1 31 323 INV
|
||||
1 1 55 317 INV
|
||||
1 1 18 381 INV
|
||||
1 1 60 400 INV
|
||||
1 1 5 322 INV
|
||||
1 1 14 395 INV
|
||||
1 1 47 402 INV
|
||||
1 1 8 347 INV
|
||||
1 1 19 385 INV
|
||||
1 1 53 374 INV
|
||||
1 1 29 330 INV
|
||||
1 1 1 382 INV
|
||||
1 1 34 344 INV
|
||||
1 1 20 333 INV
|
||||
1 1 37 321 INV
|
||||
1 1 45 390 INV
|
||||
1 1 11 338 INV
|
||||
1 1 42 376 INV
|
||||
1 1 12 370 INV
|
||||
1 1 38 388 INV
|
||||
1 1 23 318 INV
|
||||
1 1 41 392 INV
|
||||
1 1 61 329 INV
|
||||
1 1 15 403 INV
|
||||
1 1 48 396 INV
|
||||
1 1 26 320 INV
|
||||
1 1 43 337 INV
|
||||
1 1 59 334 INV
|
||||
1 1 9 393 INV
|
||||
1 1 58 319 INV
|
||||
1 1 17 326 INV
|
||||
1 1 44 369 INV
|
||||
1 1 21 375 INV
|
||||
1 1 49 325 INV
|
||||
1 1 16 397 INV
|
||||
1 1 25 405 INV
|
||||
1 1 51 384 INV
|
||||
1 1 4 316 INV
|
||||
1 1 2 345 INV
|
||||
1 1 39 386 INV
|
||||
1 1 46 394 INV
|
||||
1 1 35 313 INV
|
||||
1 1 22 328 INV
|
||||
1 1 63 324 INV
|
||||
1 1 33 383 INV
|
||||
1 1 30 379 INV
|
||||
2 1 313 314 282 AND
|
||||
2 1 315 316 283 AND
|
||||
2 1 317 318 284 AND
|
||||
2 1 319 320 299 AND
|
||||
2 1 321 322 285 AND
|
||||
2 1 323 324 286 AND
|
||||
2 1 325 326 288 AND
|
||||
2 1 327 328 289 AND
|
||||
2 1 329 330 290 AND
|
||||
1 1 331 130 INV
|
||||
2 1 332 333 287 AND
|
||||
2 1 334 335 292 AND
|
||||
1 1 336 256 INV
|
||||
2 1 337 338 293 AND
|
||||
1 1 339 123 INV
|
||||
1 1 340 214 INV
|
||||
1 1 341 116 INV
|
||||
1 1 342 228 INV
|
||||
1 1 343 276 INV
|
||||
2 1 344 345 310 AND
|
||||
2 1 346 347 300 AND
|
||||
1 1 348 235 INV
|
||||
1 1 349 88 INV
|
||||
1 1 350 151 INV
|
||||
1 1 351 270 INV
|
||||
1 1 352 193 INV
|
||||
1 1 353 158 INV
|
||||
1 1 354 109 INV
|
||||
1 1 355 207 INV
|
||||
1 1 356 263 INV
|
||||
1 1 357 66 INV
|
||||
1 1 358 95 INV
|
||||
1 1 359 172 INV
|
||||
1 1 360 179 INV
|
||||
1 1 361 165 INV
|
||||
1 1 362 81 INV
|
||||
1 1 363 221 INV
|
||||
1 1 364 74 INV
|
||||
1 1 365 242 INV
|
||||
1 1 366 186 INV
|
||||
1 1 367 144 INV
|
||||
1 1 368 102 INV
|
||||
2 1 369 370 301 AND
|
||||
1 1 371 137 INV
|
||||
1 1 372 200 INV
|
||||
1 1 373 249 INV
|
||||
2 1 374 375 298 AND
|
||||
2 1 376 377 296 AND
|
||||
2 1 378 379 291 AND
|
||||
2 1 380 381 297 AND
|
||||
2 1 382 383 306 AND
|
||||
2 1 384 385 294 AND
|
||||
2 1 386 387 295 AND
|
||||
2 1 388 389 302 AND
|
||||
2 1 390 391 303 AND
|
||||
2 1 392 393 304 AND
|
||||
2 1 394 395 305 AND
|
||||
2 1 396 397 307 AND
|
||||
2 1 398 399 308 AND
|
||||
2 1 400 401 309 AND
|
||||
2 1 402 403 311 AND
|
||||
2 1 404 405 312 AND
|
||||
1 1 282 266 INV
|
||||
1 1 283 259 INV
|
||||
1 1 284 126 INV
|
||||
1 1 285 252 INV
|
||||
1 1 286 69 INV
|
||||
1 1 287 147 INV
|
||||
1 1 288 168 INV
|
||||
1 1 289 133 INV
|
||||
1 1 290 84 INV
|
||||
1 1 291 77 INV
|
||||
1 1 292 98 INV
|
||||
1 1 293 210 INV
|
||||
1 1 294 154 INV
|
||||
1 1 295 238 INV
|
||||
1 1 296 217 INV
|
||||
1 1 297 161 INV
|
||||
1 1 298 140 INV
|
||||
1 1 299 105 INV
|
||||
1 1 300 231 INV
|
||||
1 1 301 203 INV
|
||||
1 1 302 245 INV
|
||||
1 1 303 196 INV
|
||||
1 1 304 224 INV
|
||||
1 1 305 189 INV
|
||||
1 1 306 281 INV
|
||||
1 1 307 175 INV
|
||||
1 1 308 119 INV
|
||||
1 1 309 91 INV
|
||||
1 1 310 273 INV
|
||||
1 1 311 182 INV
|
||||
1 1 312 112 INV
|
||||
2 1 281 276 277 AND
|
||||
2 1 69 66 279 AND
|
||||
2 1 281 278 280 AND
|
||||
2 1 277 278 407 XOR
|
||||
1 1 279 71 INV
|
||||
1 1 280 275 INV
|
||||
2 1 275 276 274 AND
|
||||
1 1 274 271 INV
|
||||
2 1 2 271 268 XOR
|
||||
2 1 271 273 272 AND
|
||||
2 1 34 268 408 XOR
|
||||
1 1 272 269 INV
|
||||
2 1 269 270 267 AND
|
||||
1 1 267 264 INV
|
||||
2 1 3 264 261 XOR
|
||||
2 1 264 266 265 AND
|
||||
2 1 35 261 409 XOR
|
||||
1 1 265 262 INV
|
||||
2 1 262 263 260 AND
|
||||
1 1 260 257 INV
|
||||
2 1 4 257 253 XOR
|
||||
2 1 257 259 258 AND
|
||||
2 1 36 253 410 XOR
|
||||
1 1 258 255 INV
|
||||
2 1 255 256 254 AND
|
||||
1 1 254 250 INV
|
||||
2 1 5 250 247 XOR
|
||||
2 1 250 252 251 AND
|
||||
2 1 37 247 411 XOR
|
||||
1 1 251 248 INV
|
||||
2 1 248 249 246 AND
|
||||
1 1 246 243 INV
|
||||
2 1 6 243 239 XOR
|
||||
2 1 243 245 244 AND
|
||||
2 1 38 239 412 XOR
|
||||
1 1 244 241 INV
|
||||
2 1 241 242 240 AND
|
||||
1 1 240 236 INV
|
||||
2 1 7 236 233 XOR
|
||||
2 1 236 238 237 AND
|
||||
2 1 39 233 413 XOR
|
||||
1 1 237 234 INV
|
||||
2 1 234 235 232 AND
|
||||
1 1 232 229 INV
|
||||
2 1 8 229 226 XOR
|
||||
2 1 229 231 230 AND
|
||||
2 1 40 226 414 XOR
|
||||
1 1 230 227 INV
|
||||
2 1 227 228 225 AND
|
||||
1 1 225 222 INV
|
||||
2 1 9 222 219 XOR
|
||||
2 1 222 224 223 AND
|
||||
2 1 41 219 415 XOR
|
||||
1 1 223 220 INV
|
||||
2 1 220 221 218 AND
|
||||
1 1 218 215 INV
|
||||
2 1 10 215 212 XOR
|
||||
2 1 215 217 216 AND
|
||||
2 1 42 212 416 XOR
|
||||
1 1 216 213 INV
|
||||
2 1 213 214 211 AND
|
||||
1 1 211 208 INV
|
||||
2 1 11 208 205 XOR
|
||||
2 1 208 210 209 AND
|
||||
2 1 43 205 417 XOR
|
||||
1 1 209 206 INV
|
||||
2 1 206 207 204 AND
|
||||
1 1 204 201 INV
|
||||
2 1 12 201 198 XOR
|
||||
2 1 201 203 202 AND
|
||||
2 1 44 198 418 XOR
|
||||
1 1 202 199 INV
|
||||
2 1 199 200 197 AND
|
||||
1 1 197 195 INV
|
||||
2 1 13 195 190 XOR
|
||||
2 1 195 196 194 AND
|
||||
2 1 45 190 419 XOR
|
||||
1 1 194 192 INV
|
||||
2 1 192 193 191 AND
|
||||
1 1 191 187 INV
|
||||
2 1 14 187 183 XOR
|
||||
2 1 187 189 188 AND
|
||||
2 1 46 183 420 XOR
|
||||
1 1 188 185 INV
|
||||
2 1 185 186 184 AND
|
||||
1 1 184 180 INV
|
||||
2 1 15 180 177 XOR
|
||||
2 1 180 182 181 AND
|
||||
2 1 47 177 421 XOR
|
||||
1 1 181 178 INV
|
||||
2 1 178 179 176 AND
|
||||
1 1 176 173 INV
|
||||
2 1 48 173 170 XOR
|
||||
2 1 173 175 174 AND
|
||||
2 1 16 170 422 XOR
|
||||
1 1 174 171 INV
|
||||
2 1 171 172 169 AND
|
||||
1 1 169 166 INV
|
||||
2 1 17 166 163 XOR
|
||||
2 1 166 168 167 AND
|
||||
2 1 49 163 423 XOR
|
||||
1 1 167 164 INV
|
||||
2 1 164 165 162 AND
|
||||
1 1 162 159 INV
|
||||
2 1 18 159 156 XOR
|
||||
2 1 159 161 160 AND
|
||||
2 1 50 156 424 XOR
|
||||
1 1 160 157 INV
|
||||
2 1 157 158 155 AND
|
||||
1 1 155 152 INV
|
||||
2 1 19 152 149 XOR
|
||||
2 1 152 154 153 AND
|
||||
2 1 51 149 425 XOR
|
||||
1 1 153 150 INV
|
||||
2 1 150 151 148 AND
|
||||
1 1 148 145 INV
|
||||
2 1 20 145 141 XOR
|
||||
2 1 145 147 146 AND
|
||||
2 1 52 141 426 XOR
|
||||
1 1 146 143 INV
|
||||
2 1 143 144 142 AND
|
||||
1 1 142 138 INV
|
||||
2 1 53 138 135 XOR
|
||||
2 1 138 140 139 AND
|
||||
2 1 21 135 427 XOR
|
||||
1 1 139 136 INV
|
||||
2 1 136 137 134 AND
|
||||
1 1 134 132 INV
|
||||
2 1 22 132 127 XOR
|
||||
2 1 132 133 131 AND
|
||||
2 1 54 127 428 XOR
|
||||
1 1 131 129 INV
|
||||
2 1 129 130 128 AND
|
||||
1 1 128 124 INV
|
||||
2 1 23 124 121 XOR
|
||||
2 1 124 126 125 AND
|
||||
2 1 55 121 429 XOR
|
||||
1 1 125 122 INV
|
||||
2 1 122 123 120 AND
|
||||
1 1 120 117 INV
|
||||
2 1 24 117 114 XOR
|
||||
2 1 117 119 118 AND
|
||||
2 1 56 114 430 XOR
|
||||
1 1 118 115 INV
|
||||
2 1 115 116 113 AND
|
||||
1 1 113 110 INV
|
||||
2 1 25 110 107 XOR
|
||||
2 1 110 112 111 AND
|
||||
2 1 57 107 431 XOR
|
||||
1 1 111 108 INV
|
||||
2 1 108 109 106 AND
|
||||
1 1 106 103 INV
|
||||
2 1 26 103 100 XOR
|
||||
2 1 103 105 104 AND
|
||||
2 1 58 100 432 XOR
|
||||
1 1 104 101 INV
|
||||
2 1 101 102 99 AND
|
||||
1 1 99 96 INV
|
||||
2 1 59 96 93 XOR
|
||||
2 1 96 98 97 AND
|
||||
2 1 27 93 433 XOR
|
||||
1 1 97 94 INV
|
||||
2 1 94 95 92 AND
|
||||
1 1 92 89 INV
|
||||
2 1 28 89 86 XOR
|
||||
2 1 89 91 90 AND
|
||||
2 1 60 86 434 XOR
|
||||
1 1 90 87 INV
|
||||
2 1 87 88 85 AND
|
||||
1 1 85 83 INV
|
||||
2 1 61 83 79 XOR
|
||||
2 1 83 84 82 AND
|
||||
2 1 29 79 435 XOR
|
||||
1 1 82 80 INV
|
||||
2 1 80 81 78 AND
|
||||
1 1 78 76 INV
|
||||
2 1 30 76 72 XOR
|
||||
2 1 76 77 75 AND
|
||||
2 1 62 72 436 XOR
|
||||
1 1 75 73 INV
|
||||
2 1 73 74 70 AND
|
||||
2 1 70 71 437 XOR
|
||||
1 1 70 68 INV
|
||||
2 1 68 69 67 AND
|
||||
1 1 67 65 INV
|
||||
2 1 65 66 64 AND
|
||||
1 1 64 438 INV
|
||||
`;
|
||||
const inputBitsPerParty = new Array(size).fill(0);
|
||||
inputBitsPerParty[0] = 512;
|
||||
|
||||
const outputBits = await Promise.all(new Array(size).fill(0).map((_0, party) => secureMPC({
|
||||
party,
|
||||
size,
|
||||
circuit: sha1Circuit,
|
||||
inputBits: (() => {
|
||||
if (party === 0) {
|
||||
const bits = new Uint8Array(512);
|
||||
bits[0] = 1; // A single leading 1 to make a valid sha1 block.
|
||||
return bits;
|
||||
}
|
||||
|
||||
return new Uint8Array(0);
|
||||
})(),
|
||||
inputBitsPerParty,
|
||||
io: {
|
||||
send: (toParty, channel, data) => {
|
||||
bqs.get(party, toParty, channel).push(data);
|
||||
},
|
||||
recv: async (fromParty, channel, min_len, max_len) => {
|
||||
return bqs.get(fromParty, party, channel).pop(min_len, max_len);
|
||||
},
|
||||
}
|
||||
})));
|
||||
|
||||
return outputBits.map(bits => bitsToHex(bits));
|
||||
}
|
||||
|
||||
function bitsToHex(bits: Uint8Array): string {
|
||||
if (bits.length % 8 !== 0) {
|
||||
throw new Error('Invalid number of bits.');
|
||||
}
|
||||
|
||||
let hexParts: string[] = [];
|
||||
|
||||
for (let i = 0; i < bits.length; i += 4) {
|
||||
let nibble = 0;
|
||||
|
||||
for (let j = 0; j < 4; j++) {
|
||||
nibble |= bits[i + j] << (3 - j);
|
||||
}
|
||||
|
||||
hexParts.push(nibble.toString(16));
|
||||
}
|
||||
|
||||
return hexParts.join('');
|
||||
}
|
||||
|
||||
async function getCircuit(name: string) {
|
||||
const txt = await fs.readFile(
|
||||
import.meta.resolve(`../circuits/${name}`).slice(7),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user