Skip to content

Commit

Permalink
feat: updating nodejs masp param storage
Browse files Browse the repository at this point in the history
  • Loading branch information
jurevans committed Jan 29, 2025
1 parent b35f714 commit 1a336ab
Show file tree
Hide file tree
Showing 6 changed files with 359 additions and 16 deletions.
61 changes: 61 additions & 0 deletions packages/shared/lib/src/sdk/masp/masp.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,71 @@ function existsSync(path) {
return fs.existsSync(path);
}

const MASP_MPC_RELEASE_URL =
"https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/";

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

async function hasMaspParams() {
return (
(await has(MaspParam.Spend)) &&
(await has(MaspParam.Output)) &&
(await has(MaspParam.Convert))
);
}

async function fetchAndStoreMaspParams(url) {
return Promise.all([
fetchAndStore(MaspParam.Spend, url),
fetchAndStore(MaspParam.Output, url),
fetchAndStore(MaspParam.Convert, url),
]);
}

async function getMaspParams() {
return Promise.all([
get(MaspParam.Spend),
get(MaspParam.Output),
get(MaspParam.Convert),
]);
}

async function fetchAndStore(param, url) {
return await fetchParams(param, url)
.then((data) => set(param, data))
.catch((e) => {
return Promise.reject(`Encountered errors fetching ${param}: ${e}`);
});
}

async function fetchParams(param, url) {
if (!url) {
url = MASP_MPC_RELEASE_URL;
}
return fetch(`${url}${param}`)
.then((response) => response.arrayBuffer())
.then((ab) => {
const bytes = new Uint8Array(ab);
return validateMaspParamBytes({ param, bytes });
});
}

async function set(path, data) {
console.log({ path, data });
console.warn("TODO: IMPLEMENT FOR NODEJS!");
}

module.exports = {
writeFileSync,
readFileSync,
renameSync,
unlinkSync,
existsSync,
fetchAndStoreMaspParams,
hasMaspParams,
getMaspParams,
};
226 changes: 226 additions & 0 deletions packages/shared/lib/src/sdk/masp/masp.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
const PREFIX = "Namada::SDK";
const MASP_MPC_RELEASE_URL =
"https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/";

const sha256Hash = async (msg: Uint8Array): Promise<string> => {
const hashBuffer = await crypto.subtle.digest("SHA-256", msg);
const hashArray = Array.from(new Uint8Array(hashBuffer));
// Return hash as hex
return hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
};

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

type MaspParamBytes = {
param: MaspParam;
bytes: Uint8Array;
};

/**
* The following sha256 digests where produced by downloading the following:
* https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params
* https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params
* https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params
*
* And running "sha256sum" against each file:
*
* > sha256sum masp-convert.params
* 8e049c905e0e46f27662c7577a4e3480c0047ee1171f7f6d9c5b0de757bf71f1 masp-convert.params
*
* > sha256sum masp-spend.params
* 62b3c60ca54bd99eb390198e949660624612f7db7942db84595fa9f1b4a29fd8 masp-spend.params
*
* > sha256sum masp-output.params
* ed8b5d354017d808cfaf7b31eca5c511936e65ef6d276770251f5234ec5328b8 masp-output.params
*
* Length is specified in bytes, and can be retrieved with:
*
* > wc -c < masp-convert.params
* 22570940
* > wc -c < masp-spend.params
* 49848572
* > wc -c < masp-output.params
* 16398620
*/
const MASP_PARAM_ATTR: Record<
MaspParam,
{ length: number; sha256sum: string }
> = {
[MaspParam.Output]: {
length: 16398620,
sha256sum:
"ed8b5d354017d808cfaf7b31eca5c511936e65ef6d276770251f5234ec5328b8",
},
[MaspParam.Spend]: {
length: 49848572,
sha256sum:
"62b3c60ca54bd99eb390198e949660624612f7db7942db84595fa9f1b4a29fd8",
},
[MaspParam.Convert]: {
length: 22570940,
sha256sum:
"8e049c905e0e46f27662c7577a4e3480c0047ee1171f7f6d9c5b0de757bf71f1",
},
};

const validateMaspParamBytes = async ({
param,
bytes,
}: MaspParamBytes): Promise<Uint8Array> => {
const { length, sha256sum } = MASP_PARAM_ATTR[param];

// Reject if invalid length (incomplete download or invalid)
console.info(`Validating data length for ${param}, expecting ${length}...`);

if (length !== bytes.length) {
return Promise.reject(
`[${param}]: Invalid data length! Expected ${length}, received ${bytes.length}!`
);
}

// Reject if invalid hash (otherwise invalid data)
console.info(`Validating sha256sum for ${param}, expecting ${sha256sum}...`);
const hash = await sha256Hash(bytes);

if (hash !== sha256sum) {
return Promise.reject(
`[${param}]: Invalid sha256sum! Expected ${sha256sum}, received ${hash}!`
);
}

return bytes;
};

export async function hasMaspParams(): Promise<boolean> {
return (
(await has(MaspParam.Spend)) &&
(await has(MaspParam.Output)) &&
(await has(MaspParam.Convert))
);
}

export async function fetchAndStoreMaspParams(
url?: string
): Promise<[void, void, void]> {
return Promise.all([
fetchAndStore(MaspParam.Spend, url),
fetchAndStore(MaspParam.Output, url),
fetchAndStore(MaspParam.Convert, url),
]);
}

export async function getMaspParams(): Promise<[unknown, unknown, unknown]> {
return Promise.all([
get(MaspParam.Spend),
get(MaspParam.Output),
get(MaspParam.Convert),
]);
}

export async function fetchAndStore(
param: MaspParam,
url?: string
): Promise<void> {
return await fetchParams(param, url)
.then((data) => set(param, data))
.catch((e) => {
return Promise.reject(`Encountered errors fetching ${param}: ${e}`);
});
}

export async function fetchParams(
param: MaspParam,
url: string = MASP_MPC_RELEASE_URL
): Promise<Uint8Array> {
return fetch(`${url}${param}`)
.then((response) => response.arrayBuffer())
.then((ab) => {
const bytes = new Uint8Array(ab);
return validateMaspParamBytes({ param, bytes });
});
}

function getDB(): Promise<IDBDatabase> {
return 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/no-explicit-any
const db = (event.target as any).result;

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

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

export async function get(key: string): Promise<unknown> {
const tx = (await getDB()).transaction(PREFIX, "readonly");
const store = tx.objectStore(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);
}
};
});
}

export async function has(key: string): Promise<boolean> {
const tx = (await getDB()).transaction(PREFIX, "readonly");
const store = tx.objectStore(PREFIX);

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

reject(event.target);
};
request.onsuccess = (e) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const cursor = (e.target as any).result;
resolve(!!cursor);
};
});
}

export async function set(key: string, data: unknown): Promise<void> {
const tx = (await getDB()).transaction(PREFIX, "readwrite");
const store = tx.objectStore(PREFIX);

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

reject(event.target);
};
request.onsuccess = () => {
resolve();
};
});
}
24 changes: 24 additions & 0 deletions packages/shared/lib/src/sdk/masp/masp_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,22 @@ fn file_exists(path: PathBuf) -> bool {
.unwrap()
}

pub async fn has_masp_params() -> Result<JsValue, JsValue> {
let has = js_has_masp_params().await?;

Ok(js_sys::Boolean::from(has.as_bool().unwrap()).into())
}

pub async fn fetch_and_store_masp_params(url: Option<String>) -> Result<(), JsValue> {
js_fetch_and_store_masp_params(url).await?;
Ok(())
}

pub async fn get_masp_params() -> Result<JsValue, JsValue> {
let params = js_get_masp_params().await?;
Ok(params)
}

#[wasm_bindgen(module = "/src/sdk/masp/masp.node.js")]
extern "C" {
#[wasm_bindgen(catch, js_name = "writeFileSync")]
Expand All @@ -202,4 +218,12 @@ extern "C" {

#[wasm_bindgen(catch, js_name = "existsSync")]
fn exists_sync(path: JsValue) -> Result<JsValue, JsValue>;

// MASP Param methods
#[wasm_bindgen(catch, js_name = "getMaspParams")]
async fn js_get_masp_params() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "hasMaspParams")]
async fn js_has_masp_params() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "fetchAndStoreMaspParams")]
async fn js_fetch_and_store_masp_params(url: Option<String>) -> Result<JsValue, JsValue>;
}
28 changes: 27 additions & 1 deletion packages/shared/lib/src/sdk/masp/masp_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use namada_sdk::masp::{ContextSyncStatus, DispatcherCache, ShieldedUtils};
use namada_sdk::masp_proofs::prover::LocalTxProver;
use namada_sdk::ShieldedWallet;
use rexie::{Error, ObjectStore, Rexie, TransactionMode};
use wasm_bindgen::{JsError, JsValue};
use wasm_bindgen::{prelude::*, JsError, JsValue};

use crate::utils::to_bytes;

Expand Down Expand Up @@ -265,3 +265,29 @@ impl ShieldedUtils for WebShieldedUtils {
DispatcherCache::deserialize(&mut &stored_cache_bytes[..])
}
}

pub async fn has_masp_params() -> Result<JsValue, JsValue> {
let has = js_has_masp_params().await?;

Ok(js_sys::Boolean::from(has.as_bool().unwrap()).into())
}

pub async fn fetch_and_store_masp_params(url: Option<String>) -> Result<(), JsValue> {
js_fetch_and_store_masp_params(url).await?;
Ok(())
}

pub async fn get_masp_params() -> Result<JsValue, JsValue> {
let params = js_get_masp_params().await?;
Ok(params)
}

#[wasm_bindgen(module = "/src/sdk/masp/masp.web.js")]
extern "C" {
#[wasm_bindgen(catch, js_name = "getMaspParams")]
async fn js_get_masp_params() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "hasMaspParams")]
async fn js_has_masp_params() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "fetchAndStoreMaspParams")]
async fn js_fetch_and_store_masp_params(url: Option<String>) -> Result<JsValue, JsValue>;
}
10 changes: 8 additions & 2 deletions packages/shared/lib/src/sdk/masp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
mod masp_web;

#[cfg(feature = "web")]
pub use masp_web::WebShieldedUtils as JSShieldedUtils;
pub use masp_web::{
fetch_and_store_masp_params, get_masp_params, has_masp_params,
WebShieldedUtils as JSShieldedUtils,
};

#[cfg(feature = "nodejs")]
mod masp_node;

#[cfg(feature = "nodejs")]
pub use masp_node::NodeShieldedUtils as JSShieldedUtils;
pub use masp_node::{
fetch_and_store_masp_params, get_masp_params, has_masp_params,
NodeShieldedUtils as JSShieldedUtils,
};

pub mod sync;
Loading

0 comments on commit 1a336ab

Please sign in to comment.