2023-06-23 05:23:56 +00:00
|
|
|
// @ts-ignore
|
2023-06-24 03:41:27 +00:00
|
|
|
import baoWasm from "./wasm.js";
|
2023-06-24 00:43:51 +00:00
|
|
|
import Go from "./go_wasm.js";
|
2023-06-23 05:23:56 +00:00
|
|
|
|
|
|
|
export async function getVerifiableStream(
|
|
|
|
root: Uint8Array,
|
|
|
|
proof: Uint8Array,
|
|
|
|
data: ReadableStream,
|
|
|
|
) {
|
|
|
|
const wasm = await getWasmInstance();
|
|
|
|
// @ts-ignore
|
|
|
|
const reader = new VariableChunkStream(data);
|
|
|
|
let bytesToRead;
|
|
|
|
|
2023-06-24 08:10:09 +00:00
|
|
|
if (root instanceof ArrayBuffer) {
|
|
|
|
root = new Uint8Array(root);
|
|
|
|
}
|
|
|
|
if (proof instanceof ArrayBuffer) {
|
|
|
|
proof = new Uint8Array(proof);
|
|
|
|
}
|
|
|
|
|
2023-06-23 05:23:56 +00:00
|
|
|
const getNextBytes = async () => {
|
|
|
|
bytesToRead = getWasmProperty(wasmId, "write_promise");
|
|
|
|
bytesToRead = await bytesToRead;
|
|
|
|
};
|
|
|
|
|
|
|
|
const callExports = (name: string) => {
|
|
|
|
// @ts-ignore
|
|
|
|
return wasm.exports[name]();
|
|
|
|
};
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
const exit = () => {
|
|
|
|
callExports("exit");
|
|
|
|
cleanup();
|
|
|
|
};
|
|
|
|
|
|
|
|
const done = (controller: ReadableStreamDefaultController) => {
|
|
|
|
controller.close();
|
|
|
|
exit();
|
|
|
|
};
|
|
|
|
|
|
|
|
const cleanup = () => {
|
|
|
|
const win = getWin();
|
|
|
|
const props = Object.getOwnPropertyNames(win);
|
|
|
|
|
|
|
|
props
|
|
|
|
.filter((item) => item.startsWith(`bao_${wasmId}`))
|
|
|
|
.forEach((item) => {
|
|
|
|
delete win[item];
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
const wasmId = callExports("start");
|
|
|
|
getWasmProperty(wasmId, "set_root")(root);
|
|
|
|
getWasmProperty(wasmId, "set_proof")(proof);
|
2023-06-24 07:34:53 +00:00
|
|
|
await getNextBytes();
|
2023-06-23 05:23:56 +00:00
|
|
|
|
|
|
|
return new ReadableStream({
|
|
|
|
async pull(controller) {
|
|
|
|
let chunk;
|
|
|
|
|
|
|
|
try {
|
|
|
|
chunk = await reader.read(bytesToRead);
|
|
|
|
if (chunk.value) {
|
|
|
|
getWasmProperty(wasmId, "write")(chunk.value);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
// @ts-ignore
|
|
|
|
exit();
|
|
|
|
controller.error(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = getWasmProperty(wasmId, "result");
|
|
|
|
|
|
|
|
const wasmDone = result === undefined;
|
|
|
|
|
|
|
|
if (result === undefined) {
|
|
|
|
await getNextBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chunk.done || wasmDone) {
|
|
|
|
if (wasmDone) {
|
|
|
|
if (result) {
|
|
|
|
done(controller);
|
|
|
|
} else {
|
|
|
|
controller.error(getWasmProperty(wasmId, "error"));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
done(controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
async cancel(reason: any) {
|
|
|
|
await reader.cancel(reason);
|
|
|
|
exit();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function getWin() {
|
|
|
|
return globalThis || self || window;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getWasmProperty(id: number, prop: string) {
|
|
|
|
return getWin()[`bao_${id}_${prop}`];
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getWasmInstance() {
|
|
|
|
const go = new Go();
|
|
|
|
let wasm = (await baoWasm(
|
|
|
|
go.importObject,
|
|
|
|
)) as WebAssembly.WebAssemblyInstantiatedSource;
|
|
|
|
go.run(wasm);
|
|
|
|
|
|
|
|
return wasm;
|
|
|
|
}
|
|
|
|
|
|
|
|
class VariableChunkStream {
|
|
|
|
private reader: ReadableStreamDefaultReader;
|
|
|
|
private currentChunk: Uint8Array = new Uint8Array();
|
|
|
|
private currentChunkSize = 0;
|
|
|
|
private readerDone = false;
|
|
|
|
|
|
|
|
constructor(stream: ReadableStream) {
|
|
|
|
this.reader = stream.getReader();
|
|
|
|
}
|
|
|
|
|
|
|
|
async read(bytes: number) {
|
|
|
|
if (this.currentChunk.length === 0 && !this.readerDone) {
|
|
|
|
const { done, value } = await this.reader.read();
|
|
|
|
if (done) {
|
|
|
|
return { done: true };
|
|
|
|
}
|
|
|
|
this.currentChunk = value;
|
|
|
|
this.currentChunkSize = this.currentChunk.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.currentChunkSize > bytes) {
|
|
|
|
const chunk = this.currentChunk.slice(0, bytes);
|
|
|
|
this.currentChunk = this.currentChunk.slice(bytes);
|
|
|
|
this.currentChunkSize -= bytes;
|
|
|
|
return { value: chunk, done: false };
|
|
|
|
}
|
|
|
|
if (this.currentChunkSize < bytes && !this.readerDone) {
|
|
|
|
const { done, value } = await this.reader.read();
|
|
|
|
if (done) {
|
|
|
|
this.readerDone = true;
|
|
|
|
}
|
|
|
|
this.currentChunk = new Uint8Array([...this.currentChunk, ...value]);
|
|
|
|
this.currentChunkSize += value.length;
|
|
|
|
return this.read(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
const chunk = this.currentChunk;
|
|
|
|
this.currentChunk = new Uint8Array();
|
|
|
|
this.currentChunkSize = 0;
|
|
|
|
return { value: chunk, done: this.readerDone };
|
|
|
|
}
|
|
|
|
|
|
|
|
async cancel(reason: any) {
|
|
|
|
await this.reader.cancel(reason);
|
|
|
|
this.currentChunk = new Uint8Array();
|
|
|
|
this.currentChunkSize = 0;
|
|
|
|
}
|
|
|
|
}
|