Skip to content

Commit 1a336ab

Browse files
committed
feat: updating nodejs masp param storage
1 parent b35f714 commit 1a336ab

File tree

6 files changed

+359
-16
lines changed

6 files changed

+359
-16
lines changed

packages/shared/lib/src/sdk/masp/masp.node.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,71 @@ function existsSync(path) {
2121
return fs.existsSync(path);
2222
}
2323

24+
const MASP_MPC_RELEASE_URL =
25+
"https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/";
26+
27+
const MaspParam = {
28+
Output: "masp-output.params",
29+
Convert: "masp-convert.params",
30+
Spend: "masp-spend.params",
31+
};
32+
33+
async function hasMaspParams() {
34+
return (
35+
(await has(MaspParam.Spend)) &&
36+
(await has(MaspParam.Output)) &&
37+
(await has(MaspParam.Convert))
38+
);
39+
}
40+
41+
async function fetchAndStoreMaspParams(url) {
42+
return Promise.all([
43+
fetchAndStore(MaspParam.Spend, url),
44+
fetchAndStore(MaspParam.Output, url),
45+
fetchAndStore(MaspParam.Convert, url),
46+
]);
47+
}
48+
49+
async function getMaspParams() {
50+
return Promise.all([
51+
get(MaspParam.Spend),
52+
get(MaspParam.Output),
53+
get(MaspParam.Convert),
54+
]);
55+
}
56+
57+
async function fetchAndStore(param, url) {
58+
return await fetchParams(param, url)
59+
.then((data) => set(param, data))
60+
.catch((e) => {
61+
return Promise.reject(`Encountered errors fetching ${param}: ${e}`);
62+
});
63+
}
64+
65+
async function fetchParams(param, url) {
66+
if (!url) {
67+
url = MASP_MPC_RELEASE_URL;
68+
}
69+
return fetch(`${url}${param}`)
70+
.then((response) => response.arrayBuffer())
71+
.then((ab) => {
72+
const bytes = new Uint8Array(ab);
73+
return validateMaspParamBytes({ param, bytes });
74+
});
75+
}
76+
77+
async function set(path, data) {
78+
console.log({ path, data });
79+
console.warn("TODO: IMPLEMENT FOR NODEJS!");
80+
}
81+
2482
module.exports = {
2583
writeFileSync,
2684
readFileSync,
2785
renameSync,
2886
unlinkSync,
2987
existsSync,
88+
fetchAndStoreMaspParams,
89+
hasMaspParams,
90+
getMaspParams,
3091
};
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
const PREFIX = "Namada::SDK";
2+
const MASP_MPC_RELEASE_URL =
3+
"https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/";
4+
5+
const sha256Hash = async (msg: Uint8Array): Promise<string> => {
6+
const hashBuffer = await crypto.subtle.digest("SHA-256", msg);
7+
const hashArray = Array.from(new Uint8Array(hashBuffer));
8+
// Return hash as hex
9+
return hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
10+
};
11+
12+
enum MaspParam {
13+
Output = "masp-output.params",
14+
Convert = "masp-convert.params",
15+
Spend = "masp-spend.params",
16+
}
17+
18+
type MaspParamBytes = {
19+
param: MaspParam;
20+
bytes: Uint8Array;
21+
};
22+
23+
/**
24+
* The following sha256 digests where produced by downloading the following:
25+
* https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params
26+
* https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params
27+
* https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params
28+
*
29+
* And running "sha256sum" against each file:
30+
*
31+
* > sha256sum masp-convert.params
32+
* 8e049c905e0e46f27662c7577a4e3480c0047ee1171f7f6d9c5b0de757bf71f1 masp-convert.params
33+
*
34+
* > sha256sum masp-spend.params
35+
* 62b3c60ca54bd99eb390198e949660624612f7db7942db84595fa9f1b4a29fd8 masp-spend.params
36+
*
37+
* > sha256sum masp-output.params
38+
* ed8b5d354017d808cfaf7b31eca5c511936e65ef6d276770251f5234ec5328b8 masp-output.params
39+
*
40+
* Length is specified in bytes, and can be retrieved with:
41+
*
42+
* > wc -c < masp-convert.params
43+
* 22570940
44+
* > wc -c < masp-spend.params
45+
* 49848572
46+
* > wc -c < masp-output.params
47+
* 16398620
48+
*/
49+
const MASP_PARAM_ATTR: Record<
50+
MaspParam,
51+
{ length: number; sha256sum: string }
52+
> = {
53+
[MaspParam.Output]: {
54+
length: 16398620,
55+
sha256sum:
56+
"ed8b5d354017d808cfaf7b31eca5c511936e65ef6d276770251f5234ec5328b8",
57+
},
58+
[MaspParam.Spend]: {
59+
length: 49848572,
60+
sha256sum:
61+
"62b3c60ca54bd99eb390198e949660624612f7db7942db84595fa9f1b4a29fd8",
62+
},
63+
[MaspParam.Convert]: {
64+
length: 22570940,
65+
sha256sum:
66+
"8e049c905e0e46f27662c7577a4e3480c0047ee1171f7f6d9c5b0de757bf71f1",
67+
},
68+
};
69+
70+
const validateMaspParamBytes = async ({
71+
param,
72+
bytes,
73+
}: MaspParamBytes): Promise<Uint8Array> => {
74+
const { length, sha256sum } = MASP_PARAM_ATTR[param];
75+
76+
// Reject if invalid length (incomplete download or invalid)
77+
console.info(`Validating data length for ${param}, expecting ${length}...`);
78+
79+
if (length !== bytes.length) {
80+
return Promise.reject(
81+
`[${param}]: Invalid data length! Expected ${length}, received ${bytes.length}!`
82+
);
83+
}
84+
85+
// Reject if invalid hash (otherwise invalid data)
86+
console.info(`Validating sha256sum for ${param}, expecting ${sha256sum}...`);
87+
const hash = await sha256Hash(bytes);
88+
89+
if (hash !== sha256sum) {
90+
return Promise.reject(
91+
`[${param}]: Invalid sha256sum! Expected ${sha256sum}, received ${hash}!`
92+
);
93+
}
94+
95+
return bytes;
96+
};
97+
98+
export async function hasMaspParams(): Promise<boolean> {
99+
return (
100+
(await has(MaspParam.Spend)) &&
101+
(await has(MaspParam.Output)) &&
102+
(await has(MaspParam.Convert))
103+
);
104+
}
105+
106+
export async function fetchAndStoreMaspParams(
107+
url?: string
108+
): Promise<[void, void, void]> {
109+
return Promise.all([
110+
fetchAndStore(MaspParam.Spend, url),
111+
fetchAndStore(MaspParam.Output, url),
112+
fetchAndStore(MaspParam.Convert, url),
113+
]);
114+
}
115+
116+
export async function getMaspParams(): Promise<[unknown, unknown, unknown]> {
117+
return Promise.all([
118+
get(MaspParam.Spend),
119+
get(MaspParam.Output),
120+
get(MaspParam.Convert),
121+
]);
122+
}
123+
124+
export async function fetchAndStore(
125+
param: MaspParam,
126+
url?: string
127+
): Promise<void> {
128+
return await fetchParams(param, url)
129+
.then((data) => set(param, data))
130+
.catch((e) => {
131+
return Promise.reject(`Encountered errors fetching ${param}: ${e}`);
132+
});
133+
}
134+
135+
export async function fetchParams(
136+
param: MaspParam,
137+
url: string = MASP_MPC_RELEASE_URL
138+
): Promise<Uint8Array> {
139+
return fetch(`${url}${param}`)
140+
.then((response) => response.arrayBuffer())
141+
.then((ab) => {
142+
const bytes = new Uint8Array(ab);
143+
return validateMaspParamBytes({ param, bytes });
144+
});
145+
}
146+
147+
function getDB(): Promise<IDBDatabase> {
148+
return new Promise((resolve, reject) => {
149+
const request = indexedDB.open(PREFIX);
150+
request.onerror = (event) => {
151+
event.stopPropagation();
152+
reject(event.target);
153+
};
154+
155+
request.onupgradeneeded = (event) => {
156+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
157+
const db = (event.target as any).result;
158+
159+
db.createObjectStore(PREFIX, { keyPath: "key" });
160+
};
161+
162+
request.onsuccess = () => {
163+
resolve(request.result);
164+
};
165+
});
166+
}
167+
168+
export async function get(key: string): Promise<unknown> {
169+
const tx = (await getDB()).transaction(PREFIX, "readonly");
170+
const store = tx.objectStore(PREFIX);
171+
172+
return new Promise((resolve, reject) => {
173+
const request = store.get(key);
174+
request.onerror = (event) => {
175+
event.stopPropagation();
176+
177+
reject(event.target);
178+
};
179+
request.onsuccess = () => {
180+
if (!request.result) {
181+
resolve(undefined);
182+
} else {
183+
resolve(request.result.data);
184+
}
185+
};
186+
});
187+
}
188+
189+
export async function has(key: string): Promise<boolean> {
190+
const tx = (await getDB()).transaction(PREFIX, "readonly");
191+
const store = tx.objectStore(PREFIX);
192+
193+
return new Promise((resolve, reject) => {
194+
const request = store.openCursor(key);
195+
request.onerror = (event) => {
196+
event.stopPropagation();
197+
198+
reject(event.target);
199+
};
200+
request.onsuccess = (e) => {
201+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
202+
const cursor = (e.target as any).result;
203+
resolve(!!cursor);
204+
};
205+
});
206+
}
207+
208+
export async function set(key: string, data: unknown): Promise<void> {
209+
const tx = (await getDB()).transaction(PREFIX, "readwrite");
210+
const store = tx.objectStore(PREFIX);
211+
212+
return new Promise((resolve, reject) => {
213+
const request = store.put({
214+
key,
215+
data,
216+
});
217+
request.onerror = (event) => {
218+
event.stopPropagation();
219+
220+
reject(event.target);
221+
};
222+
request.onsuccess = () => {
223+
resolve();
224+
};
225+
});
226+
}

packages/shared/lib/src/sdk/masp/masp_node.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,22 @@ fn file_exists(path: PathBuf) -> bool {
186186
.unwrap()
187187
}
188188

189+
pub async fn has_masp_params() -> Result<JsValue, JsValue> {
190+
let has = js_has_masp_params().await?;
191+
192+
Ok(js_sys::Boolean::from(has.as_bool().unwrap()).into())
193+
}
194+
195+
pub async fn fetch_and_store_masp_params(url: Option<String>) -> Result<(), JsValue> {
196+
js_fetch_and_store_masp_params(url).await?;
197+
Ok(())
198+
}
199+
200+
pub async fn get_masp_params() -> Result<JsValue, JsValue> {
201+
let params = js_get_masp_params().await?;
202+
Ok(params)
203+
}
204+
189205
#[wasm_bindgen(module = "/src/sdk/masp/masp.node.js")]
190206
extern "C" {
191207
#[wasm_bindgen(catch, js_name = "writeFileSync")]
@@ -202,4 +218,12 @@ extern "C" {
202218

203219
#[wasm_bindgen(catch, js_name = "existsSync")]
204220
fn exists_sync(path: JsValue) -> Result<JsValue, JsValue>;
221+
222+
// MASP Param methods
223+
#[wasm_bindgen(catch, js_name = "getMaspParams")]
224+
async fn js_get_masp_params() -> Result<JsValue, JsValue>;
225+
#[wasm_bindgen(catch, js_name = "hasMaspParams")]
226+
async fn js_has_masp_params() -> Result<JsValue, JsValue>;
227+
#[wasm_bindgen(catch, js_name = "fetchAndStoreMaspParams")]
228+
async fn js_fetch_and_store_masp_params(url: Option<String>) -> Result<JsValue, JsValue>;
205229
}

packages/shared/lib/src/sdk/masp/masp_web.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use namada_sdk::masp::{ContextSyncStatus, DispatcherCache, ShieldedUtils};
55
use namada_sdk::masp_proofs::prover::LocalTxProver;
66
use namada_sdk::ShieldedWallet;
77
use rexie::{Error, ObjectStore, Rexie, TransactionMode};
8-
use wasm_bindgen::{JsError, JsValue};
8+
use wasm_bindgen::{prelude::*, JsError, JsValue};
99

1010
use crate::utils::to_bytes;
1111

@@ -265,3 +265,29 @@ impl ShieldedUtils for WebShieldedUtils {
265265
DispatcherCache::deserialize(&mut &stored_cache_bytes[..])
266266
}
267267
}
268+
269+
pub async fn has_masp_params() -> Result<JsValue, JsValue> {
270+
let has = js_has_masp_params().await?;
271+
272+
Ok(js_sys::Boolean::from(has.as_bool().unwrap()).into())
273+
}
274+
275+
pub async fn fetch_and_store_masp_params(url: Option<String>) -> Result<(), JsValue> {
276+
js_fetch_and_store_masp_params(url).await?;
277+
Ok(())
278+
}
279+
280+
pub async fn get_masp_params() -> Result<JsValue, JsValue> {
281+
let params = js_get_masp_params().await?;
282+
Ok(params)
283+
}
284+
285+
#[wasm_bindgen(module = "/src/sdk/masp/masp.web.js")]
286+
extern "C" {
287+
#[wasm_bindgen(catch, js_name = "getMaspParams")]
288+
async fn js_get_masp_params() -> Result<JsValue, JsValue>;
289+
#[wasm_bindgen(catch, js_name = "hasMaspParams")]
290+
async fn js_has_masp_params() -> Result<JsValue, JsValue>;
291+
#[wasm_bindgen(catch, js_name = "fetchAndStoreMaspParams")]
292+
async fn js_fetch_and_store_masp_params(url: Option<String>) -> Result<JsValue, JsValue>;
293+
}

packages/shared/lib/src/sdk/masp/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22
mod masp_web;
33

44
#[cfg(feature = "web")]
5-
pub use masp_web::WebShieldedUtils as JSShieldedUtils;
5+
pub use masp_web::{
6+
fetch_and_store_masp_params, get_masp_params, has_masp_params,
7+
WebShieldedUtils as JSShieldedUtils,
8+
};
69

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

1013
#[cfg(feature = "nodejs")]
11-
pub use masp_node::NodeShieldedUtils as JSShieldedUtils;
14+
pub use masp_node::{
15+
fetch_and_store_masp_params, get_masp_params, has_masp_params,
16+
NodeShieldedUtils as JSShieldedUtils,
17+
};
1218

1319
pub mod sync;

0 commit comments

Comments
 (0)