Skip to content

Commit

Permalink
feat: add service worker and logic to download & validate MASP params
Browse files Browse the repository at this point in the history
  • Loading branch information
jurevans committed Sep 14, 2024
1 parent 89b2ece commit 73e41c8
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 24 deletions.
5 changes: 1 addition & 4 deletions apps/namadillo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"release:dry-run": "release-it --verbose --dry-run --ci",
"release:no-npm": "release-it --verbose --no-npm.publish --ci",
"start:proxy": "node ./scripts/startProxies.js",
"dev": "vite",
"dev": "tsc --watch --project tsconfig.sw.json& vite",
"preview": "vite preview",
"dev:local": "NODE_ENV=development NAMADA_INTERFACE_LOCAL=\"true\" yarn dev",
"dev:proxy": "NAMADA_INTERFACE_PROXY=true && ./scripts/start-proxies.sh && yarn dev:local",
Expand All @@ -57,7 +57,6 @@
"test:watch": "yarn wasm:build:test && yarn jest --watchAll=true",
"test:coverage": "yarn wasm:build:test && yarn test --coverage",
"test:ci": "jest",
"test:watch-only": "yarn jest --watchAll=true",
"e2e-test": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test",
"e2e-test:headed": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test --project=chromium --headed",
"wasm:build": "node ./scripts/build.js --release",
Expand Down Expand Up @@ -107,12 +106,10 @@
"eslint-plugin-react-hooks": "^4.6.0",
"globals": "^15.9.0",
"history": "^5.3.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-create-mock-instance": "^2.0.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
"jest-transformer-svg": "^2.0.2",
"local-cors-proxy": "^1.1.0",
"postcss": "^8.4.32",
"release-it": "^17.0.1",
Expand Down
23 changes: 3 additions & 20 deletions apps/namadillo/scripts/startProxies.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,14 @@
const { exec } = require("child_process");
require("dotenv").config();

const {
NAMADA_INTERFACE_NAMADA_ALIAS = "Namada",
NAMADA_INTERFACE_NAMADA_URL,
NAMADA_INTERFACE_COSMOS_ALIAS = "Cosmos",
NAMADA_INTERFACE_COSMOS_URL,
NAMADA_INTERFACE_ETH_ALIAS = "Ethereum",
NAMADA_INTERFACE_ETH_URL,
} = process.env;
const { TRUSTED_SETUP_URL } = process.env;

const proxyConfigs = [
{
alias: NAMADA_INTERFACE_NAMADA_ALIAS,
url: NAMADA_INTERFACE_NAMADA_URL,
alias: "Trusted-Setup GitHub URL",
url: TRUSTED_SETUP_URL,
proxyPort: 8010,
},
{
alias: NAMADA_INTERFACE_COSMOS_ALIAS,
url: NAMADA_INTERFACE_COSMOS_URL,
proxyPort: 8011,
},
{
alias: NAMADA_INTERFACE_ETH_ALIAS,
url: NAMADA_INTERFACE_ETH_URL,
proxyPort: 8012,
},
];

proxyConfigs.forEach(({ alias, url, proxyPort }) => {
Expand Down
13 changes: 13 additions & 0 deletions apps/namadillo/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,16 @@ if (container) {
);
});
}

if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/sw.js", { scope: "/" })
.then((registration) => {
console.log("Service Worker registered: ", registration);
})
.catch((error) => {
console.log("Service Worker registration failed: ", error);
});
});
}
20 changes: 20 additions & 0 deletions apps/namadillo/sw/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Define constants for MASP Params Storage
*/
const STORAGE_PREFIX = "/namadillo";

enum MaspParam {
Output = "masp-output.params",
Convert = "masp-convert.params",
Spend = "masp-spend.params",
}

const MASP_PARAM_LEN: Record<MaspParam, number> = {
[MaspParam.Output]: 16398620,
[MaspParam.Spend]: 49848572,
[MaspParam.Convert]: 22570940,
};

enum MsgType {
FetchParam = "fetch-params",
}
153 changes: 153 additions & 0 deletions apps/namadillo/sw/indexedDb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* Establish types for IndexedDB
*/
interface KVStore<T> {
get<U extends T>(key: string): Promise<U | undefined>;
set<U extends T>(key: string, data: U | null): Promise<void>;
prefix(): string;
}

let durability: IDBTransactionMode = "readwrite";

class IndexedDBKVStore<T> implements KVStore<T> {
protected cachedDB?: IDBDatabase;

constructor(protected readonly _prefix: string) { }

public async get<U extends T>(key: string): Promise<U | undefined> {
const tx = (await this.getDB()).transaction([this.prefix()], "readonly");
const store = tx.objectStore(this.prefix());

return new Promise((resolve, reject) => {
const request = store.get(key);
request.onerror = (event) => {
event.stopPropagation();

reject(event.target);
};
request.onsuccess = () => {
if (!request.result) {
resolve(undefined);
} else {
resolve(request.result.data);
}
};
});
}

public async set<U extends T>(key: string, data: U | null): Promise<void> {
if (data === null) {
const tx = (await this.getDB()).transaction([this.prefix()], durability, {
durability: "strict",
});
const store = tx.objectStore(this.prefix());

return new Promise((resolve, reject) => {
const request = store.delete(key);
request.onerror = (event) => {
event.stopPropagation();

reject(event.target);
};
request.onsuccess = () => {
resolve();
};
});
} else {
const tx = (await this.getDB()).transaction([this.prefix()], durability, {
durability: "strict",
});
const store = tx.objectStore(this.prefix());

return new Promise((resolve, reject) => {
const request = store.put({
key,
data,
});
request.onerror = (event) => {
event.stopPropagation();

reject(event.target);
};
request.onsuccess = () => {
resolve();
};
});
}
}

public prefix(): string {
return this._prefix;
}

protected async getDB(): Promise<IDBDatabase> {
if (this.cachedDB) {
return this.cachedDB;
}

return new Promise((resolve, reject) => {
const request = indexedDB.open(this.prefix());
request.onerror = (event) => {
event.stopPropagation();
reject(event.target);
};

request.onupgradeneeded = (event) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const db = event.target.result;

db.createObjectStore(this.prefix(), { keyPath: "key" });
};

request.onsuccess = () => {
this.cachedDB = request.result;
resolve(request.result);
};
});
}

public static async durabilityCheck(): Promise<boolean> {
const { TARGET } = process.env;
let isDurable: boolean;

if (TARGET === "chrome") {
durability = "readwrite";
isDurable = true;
} else {
const prefix = "durability-check";
const db: IDBDatabase = await new Promise((resolve, reject) => {
const request = indexedDB.open(prefix);
request.onerror = (event) => {
event.stopPropagation();
reject(event.target);
};

request.onupgradeneeded = (event) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const db = event.target.result;

db.createObjectStore(prefix, { keyPath: "key" });
};

request.onsuccess = () => {
resolve(request.result);
};
});

try {
db.transaction([prefix], "readwriteflush" as IDBTransactionMode, {
durability: "strict",
});
durability = "readwriteflush" as IDBTransactionMode;
isDurable = true;
} catch {
durability = "readwrite";
isDurable = false;
}
}

return isDurable;
}
}
71 changes: 71 additions & 0 deletions apps/namadillo/sw/sw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
importScripts("indexedDb.js");
importScripts("constants.js");
// const { NAMADA_INTERFACE_PROXY: isProxy = false } = process.env;

const isProxy = true;
const TRUSTED_SETUP_URL =
isProxy ?
"http://localhost:8010/proxy"
: "https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup";

const store = new IndexedDBKVStore(STORAGE_PREFIX);

const fetchAndStoreMaspParam = async (maspParam: MaspParam): Promise<void> => {
console.info(`Fetching ${maspParam}...`);
return fetch([TRUSTED_SETUP_URL, maspParam].join("/"))
.then(async (response) => {
if (response.ok) {
const reader = response.body?.getReader();
if (!reader) {
throw new Error("No readable stream returned!");
}
return new ReadableStream({
start(controller) {
return pump();
function pump() {
return reader?.read().then(({ done, value }) => {
// When no more data needs to be consumed, close the stream
if (done) {
controller.close();
return;
}
// Enqueue the next data chunk into our target stream
controller.enqueue(value);
return pump();
});
}
},
});
}
})
.then((stream) => new Response(stream))
.then((response) => response.blob())
.then(async (blob) => {
const arrayBuffer = await blob.arrayBuffer();
const data = new Uint8Array(arrayBuffer);

// Validate data length:
if (data.length === MASP_PARAM_LEN[maspParam]) {
console.info(`Storing ${maspParam} => `, data);
return store.set(maspParam, data);
}
return Promise.reject(
`Failed to download ${maspParam}: ${data.length} does not match ${MASP_PARAM_LEN[maspParam]}`
);
});
};

(async () => {
const maspOutputParam = await store.get(MaspParam.Output);
console.log("Found output?: ", maspOutputParam);

const maspSpendParam = await store.get(MaspParam.Spend);
console.log("Found spend?: ", maspSpendParam);

const maspConvertParam = await store.get(MaspParam.Convert);
console.log("Found convert?", maspConvertParam);

if (!maspOutputParam) await fetchAndStoreMaspParam(MaspParam.Output);
if (!maspSpendParam) await fetchAndStoreMaspParam(MaspParam.Spend);
if (!maspConvertParam) await fetchAndStoreMaspParam(MaspParam.Convert);
})();
13 changes: 13 additions & 0 deletions apps/namadillo/tsconfig.sw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"baseUrl": "./sw",
"lib": ["ESNext", "WebWorker"],
"noEmit": false,
"strict": false,
"outDir": "public",
"target": "ESNext",
"isolatedModules": false
},
"include": ["sw"]
}

0 comments on commit 73e41c8

Please sign in to comment.