@getanthill/datastore
Version:
Event-Sourced Datastore
138 lines (110 loc) • 2.9 kB
text/typescript
import type FullyHomomorphicEncryptionClient from '../services/fhe';
import path from 'node:path';
import { Worker } from 'worker_threads';
import { ModelConfig } from '../typings';
export async function handle(
handler: string | Function,
state: any,
event: any,
modelConfig?: ModelConfig,
fhe?: FullyHomomorphicEncryptionClient,
) {
if (
modelConfig?.with_fully_homomorphic_encryption === true &&
fhe !== undefined &&
typeof handler === 'string'
) {
return handleFHE(handler, state, event, modelConfig, fhe);
}
if (typeof handler === 'function') {
return handler(state, event);
}
const res = await thread<any>(
`async function handler(state, event) {
${handler}
}`,
state,
event,
);
return res;
}
export async function handleFHE(
handler: string | Function,
state: any,
event: any,
modelConfig: ModelConfig,
fhe: FullyHomomorphicEncryptionClient,
) {
const con = fhe.constructor
.toString()
.replace(/\/\* istanbul ignore next \*\//gm, '')
.replace(/cov_[a-z0-9]+\(\)\.(b|s|f)(\[\d+\])+\+\+/gm, 'null');
const p = path.resolve(__dirname, '../../dist/');
const script = `
const SEAL = require("node-seal");
const _vm = require('vm');
_vm.default = _vm;
const _operatorOverload = require("${p}/services/fhe/operator-overload");
const _utils = require("${p}/utils");
let sealPromise = SEAL();
${con}
async function handler(state, event) {
const fhe = new FullyHomomorphicEncryptionClient({});
await fhe.connect();
const UploadedPublicKey = fhe.seal.PublicKey();
UploadedPublicKey.load(fhe.context, workerData.publicKey);
fhe.keys.public = UploadedPublicKey;
const asyncFn = fhe.compile(
\`async function handler(state, event) {
${handler}
}\`);
const res = await fhe.compute(asyncFn, state, event);
return res;
}`;
const publicKey =
event?.[modelConfig.fhe_public_key_field!] ??
state?.[modelConfig.fhe_public_key_field!] ??
modelConfig.fhe_public_key_field!;
const res = await thread<any>(script, state, event, {
publicKey,
});
return res;
}
export async function thread<T>(
script: string,
state: any,
event: any,
extra?: any,
) {
return new Promise((resolve, reject) => {
let result: T;
const workerScript = `
const { parentPort, workerData } = require('worker_threads');
${script}
async function main() {
const res = await handler(workerData.state, workerData.event);
parentPort.postMessage(JSON.stringify(res));
}
main().catch((err) => {
throw err;
});
`;
const thread = new Worker(workerScript, {
workerData: {
...extra,
state,
event,
},
eval: true,
});
thread.on('error', (err) => {
reject(err);
});
thread.on('exit', () => {
resolve(result);
});
thread.on('message', (msg) => {
result = JSON.parse(msg);
});
});
}