1#![doc = include_str!("../README.md")]
2use curvy_core::babyjubjub::{BabyJubPoint, BabyJubScalar};
16use curvy_core::cipher::{decrypt_amount_token, encrypt_amount_token};
17use curvy_core::eddsa::{
18 ScalarSignature, ScalarSigningKey, ephemeral_pub_key, pub_from_private_key_hex, sign_hex,
19 verify_scalar_compat,
20};
21use curvy_core::encoding::dec_to_biguint;
22use curvy_core::field::{Bn254Fr, Fr, fr_from_be_32_checked, fr_from_dec, fr_to_be_32, fr_to_dec};
23use curvy_core::hash_utils::sha256_bigint as core_sha256_bigint;
24use curvy_core::imt::{
25 CompletedShard, FrontierAppend, InclusionProof, IndexedMerkleTree,
26 NotesFrontier as CoreNotesFrontier, OrderedMerkleTree, OwnedNoteWitness,
27 ShardedNotesTree as CoreShardedNotesTree, TreeError, verify_proof,
28};
29use curvy_core::note;
30use curvy_core::poseidon::poseidon as core_poseidon;
31use curvy_core::stealth;
32use wasm_bindgen::prelude::*;
33
34#[cfg(feature = "wasm-threads")]
37pub use wasm_bindgen_rayon::init_thread_pool;
38
39#[wasm_bindgen]
41pub fn poseidon(inputs: Vec<String>) -> String {
42 let fes: Vec<_> = inputs.iter().map(|s| fr_from_dec(s)).collect();
43 fr_to_dec(&core_poseidon(&fes))
44}
45
46#[wasm_bindgen(js_name = ownerHash)]
48pub fn owner_hash(pub_x: String, pub_y: String, shared_secret: String) -> String {
49 fr_to_dec(¬e::owner_hash(
50 (fr_from_dec(&pub_x), fr_from_dec(&pub_y)),
51 fr_from_dec(&shared_secret),
52 ))
53}
54
55#[wasm_bindgen(js_name = noteId)]
57pub fn note_id(owner_hash: String, amount: String, token: String) -> String {
58 fr_to_dec(¬e::note_id(
59 fr_from_dec(&owner_hash),
60 fr_from_dec(&amount),
61 fr_from_dec(&token),
62 ))
63}
64
65#[wasm_bindgen]
67pub fn nullifier(shared_secret: String, pub_x: String, pub_y: String) -> String {
68 fr_to_dec(¬e::nullifier(
69 fr_from_dec(&shared_secret),
70 (fr_from_dec(&pub_x), fr_from_dec(&pub_y)),
71 ))
72}
73
74#[wasm_bindgen(js_name = pubFromPrivateKey)]
76pub fn pub_from_private_key(private_key_hex: String) -> Vec<String> {
77 let (x, y) = pub_from_private_key_hex(&private_key_hex);
78 vec![fr_to_dec(&x), fr_to_dec(&y)]
79}
80
81#[wasm_bindgen(js_name = ephemeralPubKey)]
83pub fn ephemeral_pub_key_wasm(scalar: String) -> Vec<String> {
84 let (x, y) = ephemeral_pub_key(&dec_to_biguint(&scalar));
85 vec![fr_to_dec(&x), fr_to_dec(&y)]
86}
87
88#[wasm_bindgen]
90pub fn sign(message: String, private_key_hex: String) -> Vec<String> {
91 let sig = sign_hex(&dec_to_biguint(&message), &private_key_hex);
92 vec![
93 fr_to_dec(&sig.r8.0),
94 fr_to_dec(&sig.r8.1),
95 sig.s.to_string(),
96 ]
97}
98
99#[wasm_bindgen(js_name = pubFromScalar)]
102pub fn pub_from_scalar(scalar: String) -> Result<Vec<String>, JsError> {
103 let key = ScalarSigningKey::from_decimal(&scalar).map_err(|e| JsError::new(&e.to_string()))?;
104 let public = key.verifying_key();
105 Ok(vec![fr_to_dec(&public.x()), fr_to_dec(&public.y())])
106}
107
108#[wasm_bindgen(js_name = signWithScalar)]
111pub fn sign_with_scalar(message: String, scalar: String) -> Result<Vec<String>, JsError> {
112 let message = Bn254Fr::try_from_dec(&message).map_err(|e| JsError::new(&e.to_string()))?;
113 let key = ScalarSigningKey::from_decimal(&scalar).map_err(|e| JsError::new(&e.to_string()))?;
114 let signature = key
115 .sign_curvy_v1(message)
116 .map_err(|e| JsError::new(&e.to_string()))?;
117 Ok(vec![
118 fr_to_dec(&signature.r8.x()),
119 fr_to_dec(&signature.r8.y()),
120 signature.s.to_dec(),
121 ])
122}
123
124#[wasm_bindgen(js_name = verifyScalarSignature)]
127pub fn verify_scalar_signature(
128 message: String,
129 public_x: String,
130 public_y: String,
131 r8_x: String,
132 r8_y: String,
133 s: String,
134) -> Result<bool, JsError> {
135 let message = Bn254Fr::try_from_dec(&message).map_err(|e| JsError::new(&e.to_string()))?;
136 let public = BabyJubPoint::try_from_dec(&public_x, &public_y)
137 .map_err(|e| JsError::new(&e.to_string()))?;
138 let r8 = BabyJubPoint::try_from_dec(&r8_x, &r8_y).map_err(|e| JsError::new(&e.to_string()))?;
139 let s = BabyJubScalar::try_from_dec(&s).map_err(|e| JsError::new(&e.to_string()))?;
140 Ok(verify_scalar_compat(
141 message,
142 &public,
143 &ScalarSignature { r8, s },
144 ))
145}
146
147#[wasm_bindgen(js_name = encryptAmountToken)]
149pub fn encrypt_amount_token_wasm(
150 amount: String,
151 token: String,
152 shared_secret: String,
153 ephemeral_key_x: String,
154 ephemeral_key_y: String,
155) -> Vec<String> {
156 let ss = dec_to_biguint(&shared_secret);
157 let ex = dec_to_biguint(&ephemeral_key_x);
158 let ey = dec_to_biguint(&ephemeral_key_y);
159 let out = encrypt_amount_token(fr_from_dec(&amount), fr_from_dec(&token), &ss, (&ex, &ey));
160 vec![
161 fr_to_dec(&out.encrypted_amount),
162 fr_to_dec(&out.encrypted_token),
163 ]
164}
165
166#[wasm_bindgen(js_name = decryptAmountToken)]
168pub fn decrypt_amount_token_wasm(
169 encrypted_amount: String,
170 encrypted_token: String,
171 shared_secret: String,
172 ephemeral_key_x: String,
173 ephemeral_key_y: String,
174) -> Vec<String> {
175 let ss = dec_to_biguint(&shared_secret);
176 let ex = dec_to_biguint(&ephemeral_key_x);
177 let ey = dec_to_biguint(&ephemeral_key_y);
178 let (amount, token) = decrypt_amount_token(
179 fr_from_dec(&encrypted_amount),
180 fr_from_dec(&encrypted_token),
181 &ss,
182 (&ex, &ey),
183 );
184 vec![fr_to_dec(&amount), fr_to_dec(&token)]
185}
186
187#[wasm_bindgen(js_name = sha256BigInt)]
189pub fn sha256_bigint(inputs: Vec<String>) -> String {
190 let ints: Vec<_> = inputs.iter().map(|s| dec_to_biguint(s)).collect();
191 core_sha256_bigint(&ints).to_string()
192}
193
194#[wasm_bindgen(js_name = MerkleTree)]
198pub struct WasmMerkleTree {
199 inner: IndexedMerkleTree,
200}
201
202#[wasm_bindgen(js_class = MerkleTree)]
203impl WasmMerkleTree {
204 #[wasm_bindgen(constructor)]
205 pub fn new(depth: u32) -> Result<WasmMerkleTree, JsError> {
206 Ok(Self {
207 inner: IndexedMerkleTree::new(depth as usize).map_err(js_tree_error)?,
208 })
209 }
210
211 #[wasm_bindgen(js_name = fromLeaves)]
212 pub fn from_leaves(depth: u32, packed_leaves: &[u8]) -> Result<WasmMerkleTree, JsError> {
213 Ok(Self {
214 inner: IndexedMerkleTree::from_leaves(
215 depth as usize,
216 &decode_fields(packed_leaves, "leaves")?,
217 )
218 .map_err(js_tree_error)?,
219 })
220 }
221
222 pub fn insert(&mut self, leaf: &[u8]) -> Result<u32, JsError> {
223 let index = self
224 .inner
225 .insert(decode_field(leaf, "leaf")?)
226 .map_err(js_tree_error)?;
227 Ok(index as u32)
228 }
229
230 #[wasm_bindgen(js_name = insertMany)]
231 pub fn insert_many(&mut self, packed_leaves: &[u8]) -> Result<(), JsError> {
232 self.inner
233 .insert_many(&decode_fields(packed_leaves, "leaves")?)
234 .map_err(js_tree_error)
235 }
236
237 #[wasm_bindgen(js_name = getIndex)]
238 pub fn get_index(&self, leaf: &[u8]) -> Result<Option<u32>, JsError> {
239 Ok(self
240 .inner
241 .get_index(decode_field(leaf, "leaf")?)
242 .map(|index| index as u32))
243 }
244
245 pub fn proof(&self, leaf: &[u8]) -> Result<WasmInclusionProof, JsError> {
246 Ok(WasmInclusionProof(
247 self.inner
248 .create_proof(decode_field(leaf, "leaf")?)
249 .map_err(js_tree_error)?,
250 ))
251 }
252
253 #[wasm_bindgen(js_name = proofAt)]
254 pub fn proof_at(&self, index: u32) -> Result<WasmInclusionProof, JsError> {
255 Ok(WasmInclusionProof(
256 self.inner
257 .create_proof_at(index as usize)
258 .map_err(js_tree_error)?,
259 ))
260 }
261
262 pub fn truncate(&mut self, leaf_count: u32) -> Result<(), JsError> {
263 self.inner
264 .truncate(leaf_count as usize)
265 .map_err(js_tree_error)
266 }
267
268 pub fn root(&self) -> Vec<u8> {
269 fr_to_be_32(&self.inner.root()).to_vec()
270 }
271
272 pub fn leaves(&self) -> Vec<u8> {
273 pack_fields(self.inner.leaves())
274 }
275
276 #[wasm_bindgen(getter)]
277 pub fn depth(&self) -> u32 {
278 self.inner.depth() as u32
279 }
280
281 #[wasm_bindgen(getter, js_name = leafCount)]
282 pub fn leaf_count(&self) -> u32 {
283 self.inner.leaf_count() as u32
284 }
285}
286
287#[wasm_bindgen(js_name = OrderedMerkleTree)]
289pub struct WasmOrderedMerkleTree {
290 inner: OrderedMerkleTree,
291}
292
293#[wasm_bindgen(js_class = OrderedMerkleTree)]
294impl WasmOrderedMerkleTree {
295 #[wasm_bindgen(constructor)]
296 pub fn new(depth: u32) -> Result<WasmOrderedMerkleTree, JsError> {
297 Ok(Self {
298 inner: OrderedMerkleTree::new(depth as usize).map_err(js_tree_error)?,
299 })
300 }
301
302 #[wasm_bindgen(js_name = fromLeaves)]
303 pub fn from_leaves(depth: u32, packed_leaves: &[u8]) -> Result<WasmOrderedMerkleTree, JsError> {
304 Ok(Self {
305 inner: OrderedMerkleTree::from_leaves(
306 depth as usize,
307 &decode_fields(packed_leaves, "leaves")?,
308 )
309 .map_err(js_tree_error)?,
310 })
311 }
312
313 pub fn insert(&mut self, leaf: &[u8]) -> Result<u32, JsError> {
314 let index = self
315 .inner
316 .insert(decode_field(leaf, "leaf")?)
317 .map_err(js_tree_error)?;
318 Ok(index as u32)
319 }
320
321 #[wasm_bindgen(js_name = insertMany)]
322 pub fn insert_many(&mut self, packed_leaves: &[u8]) -> Result<(), JsError> {
323 self.inner
324 .insert_many(&decode_fields(packed_leaves, "leaves")?)
325 .map_err(js_tree_error)
326 }
327
328 #[wasm_bindgen(js_name = proofAt)]
329 pub fn proof_at(&self, index: u32) -> Result<WasmInclusionProof, JsError> {
330 Ok(WasmInclusionProof(
331 self.inner
332 .create_proof_at(index as usize)
333 .map_err(js_tree_error)?,
334 ))
335 }
336
337 pub fn root(&self) -> Vec<u8> {
338 fr_to_be_32(&self.inner.root()).to_vec()
339 }
340
341 #[wasm_bindgen(getter)]
342 pub fn depth(&self) -> u32 {
343 self.inner.depth() as u32
344 }
345
346 #[wasm_bindgen(getter, js_name = leafCount)]
347 pub fn leaf_count(&self) -> u32 {
348 self.inner.leaf_count() as u32
349 }
350}
351
352#[wasm_bindgen(js_name = verifyMerkleProof)]
355pub fn verify_merkle_proof(
356 leaf: &[u8],
357 index: u32,
358 packed_siblings: &[u8],
359 root: &[u8],
360) -> Result<bool, JsError> {
361 Ok(verify_proof(&InclusionProof {
362 leaf: decode_field(leaf, "leaf")?,
363 index: index as usize,
364 siblings: decode_fields(packed_siblings, "siblings")?,
365 root: decode_field(root, "root")?,
366 }))
367}
368
369#[wasm_bindgen(js_name = NotesFrontier)]
372pub struct WasmNotesFrontier {
373 inner: CoreNotesFrontier,
374}
375
376#[wasm_bindgen(js_class = NotesFrontier)]
377impl WasmNotesFrontier {
378 #[wasm_bindgen(constructor)]
379 pub fn new(depth: u32, shard_height: u32) -> Result<WasmNotesFrontier, JsError> {
380 Ok(Self {
381 inner: CoreNotesFrontier::new(depth as usize, shard_height as usize)
382 .map_err(js_tree_error)?,
383 })
384 }
385
386 #[wasm_bindgen(js_name = production)]
389 pub fn production() -> WasmNotesFrontier {
390 Self {
391 inner: CoreNotesFrontier::production(),
392 }
393 }
394
395 #[wasm_bindgen(js_name = restore)]
396 pub fn restore(snapshot: &[u8]) -> Result<WasmNotesFrontier, JsError> {
397 Ok(Self {
398 inner: CoreNotesFrontier::from_snapshot_bytes(snapshot).map_err(js_tree_error)?,
399 })
400 }
401
402 pub fn append(&mut self, leaf: &[u8]) -> Result<WasmFrontierAppend, JsError> {
403 Ok(WasmFrontierAppend(
404 self.inner
405 .append(decode_field(leaf, "leaf")?)
406 .map_err(js_tree_error)?,
407 ))
408 }
409
410 #[wasm_bindgen(js_name = appendMany)]
411 pub fn append_many(
412 &mut self,
413 packed_leaves: &[u8],
414 ) -> Result<Vec<WasmCompletedShard>, JsError> {
415 Ok(self
416 .inner
417 .append_many(&decode_fields(packed_leaves, "leaves")?)
418 .map_err(js_tree_error)?
419 .into_iter()
420 .map(WasmCompletedShard)
421 .collect())
422 }
423
424 pub fn root(&self) -> Vec<u8> {
425 fr_to_be_32(&self.inner.root()).to_vec()
426 }
427
428 pub fn snapshot(&self) -> Vec<u8> {
429 self.inner.encode_snapshot()
430 }
431
432 #[wasm_bindgen(getter)]
433 pub fn depth(&self) -> u32 {
434 self.inner.depth() as u32
435 }
436
437 #[wasm_bindgen(getter, js_name = shardHeight)]
438 pub fn shard_height(&self) -> u32 {
439 self.inner.shard_height() as u32
440 }
441
442 #[wasm_bindgen(getter, js_name = shardSize)]
443 pub fn shard_size(&self) -> u32 {
444 self.inner.shard_size() as u32
445 }
446
447 #[wasm_bindgen(getter, js_name = leafCount)]
448 pub fn leaf_count(&self) -> u32 {
449 self.inner.leaf_count() as u32
450 }
451
452 #[wasm_bindgen(getter, js_name = shardCount)]
453 pub fn shard_count(&self) -> u32 {
454 self.inner.shard_count() as u32
455 }
456}
457
458#[wasm_bindgen(js_name = notesTreeDepth)]
461pub fn notes_tree_depth() -> u32 {
462 curvy_core::NOTES_TREE_DEPTH as u32
463}
464
465#[wasm_bindgen(js_name = notesShardHeight)]
466pub fn notes_shard_height() -> u32 {
467 curvy_core::NOTES_SHARD_HEIGHT as u32
468}
469
470#[wasm_bindgen(js_name = notesShardSize)]
471pub fn notes_shard_size() -> u32 {
472 curvy_core::NOTES_SHARD_SIZE as u32
473}
474
475#[wasm_bindgen(js_name = notesTreeVersion)]
476pub fn notes_tree_version() -> u32 {
477 curvy_core::NOTES_TREE_VERSION
478}
479
480#[wasm_bindgen(js_name = NotesFrontierAppend)]
481pub struct WasmFrontierAppend(FrontierAppend);
482
483#[wasm_bindgen(js_class = NotesFrontierAppend)]
484impl WasmFrontierAppend {
485 #[wasm_bindgen(getter, js_name = leafIndex)]
486 pub fn leaf_index(&self) -> u32 {
487 self.0.leaf_index as u32
488 }
489
490 #[wasm_bindgen(getter, js_name = hasCompletedShard)]
491 pub fn has_completed_shard(&self) -> bool {
492 self.0.completed_shard.is_some()
493 }
494
495 #[wasm_bindgen(getter, js_name = completedShardIndex)]
496 pub fn completed_shard_index(&self) -> Option<u32> {
497 self.0
498 .completed_shard
499 .as_ref()
500 .map(|shard| shard.shard_index as u32)
501 }
502
503 #[wasm_bindgen(getter, js_name = completedShardRoot)]
504 pub fn completed_shard_root(&self) -> Vec<u8> {
505 self.0
506 .completed_shard
507 .as_ref()
508 .map(|shard| fr_to_be_32(&shard.root).to_vec())
509 .unwrap_or_default()
510 }
511}
512
513#[wasm_bindgen(js_name = NotesFrontierCompletedShard)]
514pub struct WasmCompletedShard(CompletedShard);
515
516#[wasm_bindgen(js_class = NotesFrontierCompletedShard)]
517impl WasmCompletedShard {
518 #[wasm_bindgen(getter, js_name = shardIndex)]
519 pub fn shard_index(&self) -> u32 {
520 self.0.shard_index as u32
521 }
522
523 #[wasm_bindgen(getter)]
524 pub fn root(&self) -> Vec<u8> {
525 fr_to_be_32(&self.0.root).to_vec()
526 }
527}
528
529#[wasm_bindgen(js_name = ShardedNotesTree)]
533pub struct WasmShardedNotesTree {
534 inner: CoreShardedNotesTree,
535}
536
537#[wasm_bindgen(js_class = ShardedNotesTree)]
538impl WasmShardedNotesTree {
539 #[wasm_bindgen(constructor)]
540 pub fn new(depth: u32, shard_height: u32) -> Result<WasmShardedNotesTree, JsError> {
541 Ok(Self {
542 inner: CoreShardedNotesTree::new(depth as usize, shard_height as usize)
543 .map_err(js_tree_error)?,
544 })
545 }
546
547 #[wasm_bindgen(js_name = restore)]
549 pub fn restore(snapshot: &[u8]) -> Result<WasmShardedNotesTree, JsError> {
550 Ok(Self {
551 inner: CoreShardedNotesTree::from_snapshot_bytes(snapshot).map_err(js_tree_error)?,
552 })
553 }
554
555 #[wasm_bindgen(js_name = restoreParts)]
558 pub fn restore_parts(
559 depth: u32,
560 shard_height: u32,
561 packed_completed_roots: &[u8],
562 packed_live_leaves: &[u8],
563 ) -> Result<WasmShardedNotesTree, JsError> {
564 Ok(Self {
565 inner: CoreShardedNotesTree::from_parts(
566 depth as usize,
567 shard_height as usize,
568 decode_fields(packed_completed_roots, "completed shard roots")?,
569 decode_fields(packed_live_leaves, "live leaves")?,
570 )
571 .map_err(js_tree_error)?,
572 })
573 }
574
575 pub fn append(&mut self, note_id: &[u8]) -> Result<(), JsError> {
577 self.inner
578 .append(decode_field(note_id, "note id")?)
579 .map_err(js_tree_error)
580 }
581
582 #[wasm_bindgen(js_name = appendMany)]
584 pub fn append_many(&mut self, packed_note_ids: &[u8]) -> Result<(), JsError> {
585 let note_ids = decode_fields(packed_note_ids, "note ids")?;
586 self.inner.append_many(¬e_ids).map_err(js_tree_error)
587 }
588
589 #[wasm_bindgen(js_name = markOwned)]
590 pub fn mark_owned(&mut self, note_id: &[u8], leaf_index: u32) -> Result<(), JsError> {
591 self.inner
592 .mark_owned(decode_field(note_id, "note id")?, leaf_index as usize)
593 .map_err(js_tree_error)
594 }
595
596 #[wasm_bindgen(js_name = unmarkOwned)]
597 pub fn unmark_owned(&mut self, note_id: &[u8]) -> Result<bool, JsError> {
598 Ok(self.inner.unmark_owned(decode_field(note_id, "note id")?))
599 }
600
601 #[wasm_bindgen(js_name = adoptFrozenWitness)]
602 pub fn adopt_frozen_witness(
603 &mut self,
604 note_id: &[u8],
605 leaf_index: u32,
606 packed_siblings: &[u8],
607 ) -> Result<(), JsError> {
608 self.inner
609 .adopt_frozen_witness(
610 decode_field(note_id, "note id")?,
611 leaf_index as usize,
612 decode_fields(packed_siblings, "within-shard siblings")?,
613 )
614 .map_err(js_tree_error)
615 }
616
617 pub fn witness(&self, note_id: &[u8]) -> Result<WasmInclusionProof, JsError> {
618 Ok(WasmInclusionProof(
619 self.inner
620 .witness(decode_field(note_id, "note id")?)
621 .map_err(js_tree_error)?,
622 ))
623 }
624
625 #[wasm_bindgen(js_name = rewindLiveTo)]
628 pub fn rewind_live_to(&mut self, leaf_count: u32) -> Result<Vec<u8>, JsError> {
629 let removed = self
630 .inner
631 .rewind_live_to(leaf_count as usize)
632 .map_err(js_tree_error)?;
633 Ok(pack_fields(&removed))
634 }
635
636 pub fn root(&self) -> Vec<u8> {
637 fr_to_be_32(&self.inner.root()).to_vec()
638 }
639
640 #[wasm_bindgen(getter)]
641 pub fn depth(&self) -> u32 {
642 self.inner.depth() as u32
643 }
644
645 #[wasm_bindgen(getter, js_name = shardHeight)]
646 pub fn shard_height(&self) -> u32 {
647 self.inner.shard_height() as u32
648 }
649
650 #[wasm_bindgen(getter, js_name = shardSize)]
651 pub fn shard_size(&self) -> u32 {
652 self.inner.shard_size() as u32
653 }
654
655 #[wasm_bindgen(getter, js_name = leafCount)]
656 pub fn leaf_count(&self) -> u32 {
657 self.inner.leaf_count() as u32
658 }
659
660 #[wasm_bindgen(getter, js_name = completedShardCount)]
661 pub fn completed_shard_count(&self) -> u32 {
662 self.inner.completed_shard_count() as u32
663 }
664
665 #[wasm_bindgen(getter, js_name = ownedNoteCount)]
666 pub fn owned_note_count(&self) -> u32 {
667 self.inner.owned_note_count() as u32
668 }
669
670 #[wasm_bindgen(js_name = completedShardRoots)]
671 pub fn completed_shard_roots(&self) -> Vec<u8> {
672 pack_fields(self.inner.completed_roots())
673 }
674
675 #[wasm_bindgen(js_name = completedShardRoot)]
676 pub fn completed_shard_root(&self, shard_index: u32) -> Result<Vec<u8>, JsError> {
677 Ok(fr_to_be_32(
678 &self
679 .inner
680 .completed_shard_root(shard_index as usize)
681 .map_err(js_tree_error)?,
682 )
683 .to_vec())
684 }
685
686 #[wasm_bindgen(js_name = liveLeaves)]
687 pub fn live_leaves(&self) -> Vec<u8> {
688 pack_fields(self.inner.live_leaves())
689 }
690
691 #[wasm_bindgen(js_name = drainDirtyOwnedNotes)]
692 pub fn drain_dirty_owned_notes(&mut self) -> Vec<WasmOwnedNoteWitness> {
693 self.inner
694 .drain_dirty_owned_notes()
695 .into_iter()
696 .map(WasmOwnedNoteWitness)
697 .collect()
698 }
699
700 #[wasm_bindgen(js_name = ownedNotes)]
701 pub fn owned_notes(&self) -> Vec<WasmOwnedNoteWitness> {
702 self.inner
703 .owned_notes()
704 .into_iter()
705 .map(WasmOwnedNoteWitness)
706 .collect()
707 }
708
709 pub fn snapshot(&self) -> Result<Vec<u8>, JsError> {
712 self.inner.encode_snapshot().map_err(js_tree_error)
713 }
714}
715
716#[wasm_bindgen(js_name = ShardedInclusionProof)]
717pub struct WasmInclusionProof(InclusionProof);
718
719#[wasm_bindgen(js_class = ShardedInclusionProof)]
720impl WasmInclusionProof {
721 #[wasm_bindgen(getter)]
722 pub fn leaf(&self) -> Vec<u8> {
723 fr_to_be_32(&self.0.leaf).to_vec()
724 }
725
726 #[wasm_bindgen(getter)]
727 pub fn index(&self) -> u32 {
728 self.0.index as u32
729 }
730
731 #[wasm_bindgen(getter)]
732 pub fn siblings(&self) -> Vec<u8> {
733 pack_fields(&self.0.siblings)
734 }
735
736 #[wasm_bindgen(getter)]
737 pub fn root(&self) -> Vec<u8> {
738 fr_to_be_32(&self.0.root).to_vec()
739 }
740}
741
742#[wasm_bindgen(js_name = ShardedOwnedNoteWitness)]
743pub struct WasmOwnedNoteWitness(OwnedNoteWitness);
744
745#[wasm_bindgen(js_class = ShardedOwnedNoteWitness)]
746impl WasmOwnedNoteWitness {
747 #[wasm_bindgen(getter, js_name = noteId)]
748 pub fn note_id(&self) -> Vec<u8> {
749 fr_to_be_32(&self.0.note_id).to_vec()
750 }
751
752 #[wasm_bindgen(getter, js_name = leafIndex)]
753 pub fn leaf_index(&self) -> u32 {
754 self.0.leaf_index as u32
755 }
756
757 #[wasm_bindgen(getter)]
758 pub fn frozen(&self) -> bool {
759 self.0.within_shard_siblings.is_some()
760 }
761
762 #[wasm_bindgen(getter, js_name = withinShardSiblings)]
763 pub fn within_shard_siblings(&self) -> Vec<u8> {
764 self.0
765 .within_shard_siblings
766 .as_deref()
767 .map(pack_fields)
768 .unwrap_or_default()
769 }
770}
771
772fn decode_field(bytes: &[u8], what: &str) -> Result<Fr, JsError> {
773 fr_from_be_32_checked(bytes).ok_or_else(|| {
774 JsError::new(&format!(
775 "sharded tree: {what} must be one canonical 32-byte big-endian field element",
776 ))
777 })
778}
779
780fn decode_fields(bytes: &[u8], what: &str) -> Result<Vec<Fr>, JsError> {
781 if !bytes.len().is_multiple_of(32) {
782 return Err(JsError::new(&format!(
783 "sharded tree: packed {what} length {} is not divisible by 32",
784 bytes.len(),
785 )));
786 }
787 bytes
788 .chunks_exact(32)
789 .map(|raw| decode_field(raw, what))
790 .collect()
791}
792
793fn pack_fields(fields: &[Fr]) -> Vec<u8> {
794 let mut packed = Vec::with_capacity(fields.len() * 32);
795 for field in fields {
796 packed.extend_from_slice(&fr_to_be_32(field));
797 }
798 packed
799}
800
801fn js_tree_error(error: TreeError) -> JsError {
802 JsError::new(&error.to_string())
803}
804
805#[wasm_bindgen]
813pub fn version() -> String {
814 "v1.0.2".to_string()
815}
816
817#[wasm_bindgen]
819pub fn new_meta() -> Result<Vec<String>, JsError> {
820 let (k, v, big_k, big_v) = stealth::new_meta()?;
821 Ok(vec![k, v, big_k, big_v])
822}
823
824#[wasm_bindgen]
827pub fn get_meta(k: String, v: String) -> Result<Vec<String>, JsError> {
828 let (big_k, big_v) = stealth::get_meta(&k, &v)?;
829 Ok(vec![k, v, big_k, big_v])
830}
831
832#[wasm_bindgen]
836pub fn send(big_k: String, big_v: String) -> Result<Vec<String>, JsError> {
837 let (r, out) = stealth::send(&big_k, &big_v)?;
838 Ok(vec![r, out.big_r, out.view_tag, out.spending_pub_key])
839}
840
841#[wasm_bindgen]
848pub fn scan(
849 k: String,
850 v: String,
851 rs: Vec<String>,
852 view_tags: Vec<String>,
853) -> Result<Vec<ScanMatch>, JsError> {
854 Ok(stealth::scan(&k, &v, &rs, &view_tags)?
855 .into_iter()
856 .map(ScanMatch)
857 .collect())
858}
859
860#[wasm_bindgen(js_name = viewerScan)]
863pub fn viewer_scan(
864 v: String,
865 big_k: String,
866 rs: Vec<String>,
867 view_tags: Vec<String>,
868) -> Result<Vec<ViewerMatch>, JsError> {
869 Ok(stealth::viewer_scan(&v, &big_k, &rs, &view_tags)?
870 .into_iter()
871 .map(ViewerMatch)
872 .collect())
873}
874
875#[wasm_bindgen]
877pub struct ScanMatch(stealth::ScanMatch);
878
879#[wasm_bindgen]
880impl ScanMatch {
881 #[wasm_bindgen(getter)]
882 pub fn index(&self) -> u32 {
883 self.0.index
884 }
885 #[wasm_bindgen(getter, js_name = spendingPubKey)]
886 pub fn spending_pub_key(&self) -> String {
887 self.0.spending_pub_key.clone()
888 }
889 #[wasm_bindgen(getter, js_name = spendingPrivKey)]
890 pub fn spending_priv_key(&self) -> String {
891 self.0.spending_priv_key.clone()
892 }
893}
894
895#[wasm_bindgen]
897pub struct ViewerMatch(stealth::ViewerMatch);
898
899#[wasm_bindgen]
900impl ViewerMatch {
901 #[wasm_bindgen(getter)]
902 pub fn index(&self) -> u32 {
903 self.0.index
904 }
905 #[wasm_bindgen(getter, js_name = spendingPubKey)]
906 pub fn spending_pub_key(&self) -> String {
907 self.0.spending_pub_key.clone()
908 }
909}
910
911#[wasm_bindgen(js_name = dbg_isValidBN254Point)]
912pub fn dbg_is_valid_bn254_point(point: String) -> bool {
913 stealth::is_valid_bn254_point(&point)
914}
915
916#[wasm_bindgen(js_name = dbg_isValidSECP256k1Point)]
917pub fn dbg_is_valid_secp256k1_point(point: String) -> bool {
918 stealth::is_valid_secp256k1_point(&point)
919}