bindings/cashu-js nut00 and nu01

This commit is contained in:
thesimplekid
2023-09-24 09:19:50 +01:00
parent ba2e4cf1f3
commit e830cbf0b7
24 changed files with 688 additions and 1 deletions

View File

@@ -0,0 +1,22 @@
[package]
name = "cashu-js"
version = "0.1.0"
edition = "2021"
description = "Cashu rust implementation, for JavaScript"
authors = ["thesimplekid <tsk@thesimplekid.com>"]
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["lib", "cdylib"]
[dependencies]
cashu = { path = "../../crates/cashu"}
js-sys = "0.3.64"
serde-wasm-bindgen = "0.6.0"
serde_json.workspace = true
wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4.37"
[package.metadata.wasm-pack.profile.release]
wasm-opt = true

View File

@@ -0,0 +1,12 @@
use wasm_bindgen::JsValue;
pub type Result<T, E = JsValue> = std::result::Result<T, E>;
/// Helper to replace the `E` to `Error` to `napi::Error` conversion.
#[inline]
pub fn into_err<E>(error: E) -> JsValue
where
E: std::error::Error,
{
JsValue::from_str(&error.to_string())
}

View File

@@ -0,0 +1,3 @@
pub mod error;
mod nuts;
pub mod types;

View File

@@ -0,0 +1,3 @@
mod nut00;
mod nut01;
mod nut02;

View File

@@ -0,0 +1,51 @@
use std::ops::Deref;
use cashu::nuts::nut00::BlindedMessage;
use wasm_bindgen::prelude::*;
use crate::nuts::nut01::JsPublicKey;
use crate::types::amount::JsAmount;
#[wasm_bindgen(js_name = BlindedMessage)]
pub struct JsBlindedMessage {
inner: BlindedMessage,
}
impl Deref for JsBlindedMessage {
type Target = BlindedMessage;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<BlindedMessage> for JsBlindedMessage {
fn from(inner: BlindedMessage) -> JsBlindedMessage {
JsBlindedMessage { inner }
}
}
#[wasm_bindgen(js_class = BlindedMessage)]
impl JsBlindedMessage {
#[allow(clippy::new_without_default)]
#[wasm_bindgen(constructor)]
pub fn new(amount: JsAmount, b: JsPublicKey) -> Self {
Self {
inner: BlindedMessage {
amount: amount.deref().clone(),
b: b.deref().clone(),
},
}
}
/// Amount
#[wasm_bindgen(getter)]
pub fn amount(&self) -> JsAmount {
self.inner.amount.into()
}
/// B
#[wasm_bindgen(getter)]
pub fn b(&self) -> JsPublicKey {
self.inner.b.clone().into()
}
}

View File

@@ -0,0 +1,39 @@
use std::ops::Deref;
use wasm_bindgen::prelude::*;
use cashu::nuts::nut00::wallet::BlindedMessages;
use crate::error::{into_err, Result};
use crate::types::JsAmount;
#[wasm_bindgen(js_name = BlindedMessages)]
pub struct JsBlindedMessages {
inner: BlindedMessages,
}
impl Deref for JsBlindedMessages {
type Target = BlindedMessages;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[wasm_bindgen(js_class = BlindedMessages)]
impl JsBlindedMessages {
#[wasm_bindgen(js_name = random)]
pub fn random(amount: JsAmount) -> Result<JsBlindedMessages> {
Ok(JsBlindedMessages {
inner: BlindedMessages::random(*amount.deref()).map_err(into_err)?,
})
}
#[wasm_bindgen(js_name = blank)]
pub fn blank(fee_reserve: JsAmount) -> Result<JsBlindedMessages> {
Ok(JsBlindedMessages {
inner: BlindedMessages::blank(*fee_reserve.deref()).map_err(into_err)?,
})
}
// TODO: Gettters
}

View File

@@ -0,0 +1,33 @@
use std::ops::Deref;
use cashu::nuts::nut00::BlindedSignature;
use wasm_bindgen::prelude::*;
use crate::{nuts::nut01::JsPublicKey, nuts::nut02::JsId, types::JsAmount};
#[wasm_bindgen(js_name = BlindedSignature)]
pub struct JsBlindedSignature {
inner: BlindedSignature,
}
impl Deref for JsBlindedSignature {
type Target = BlindedSignature;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[wasm_bindgen(js_class = BlindedSignature)]
impl JsBlindedSignature {
#[allow(clippy::new_without_default)]
#[wasm_bindgen(constructor)]
pub fn new(id: JsId, amount: JsAmount, c: JsPublicKey) -> Self {
Self {
inner: BlindedSignature {
id: id.deref().clone(),
amount: amount.deref().clone(),
c: c.deref().clone(),
},
}
}
}

View File

@@ -0,0 +1,10 @@
mod blinded_message;
mod blinded_messages;
mod blinded_signature;
mod proof;
mod token;
pub use blinded_message::JsBlindedMessage;
pub use blinded_messages::JsBlindedMessages;
pub use blinded_signature::JsBlindedSignature;
pub use proof::JsProof;

View File

@@ -0,0 +1,63 @@
use std::ops::Deref;
use cashu::nuts::nut00::Proof;
use wasm_bindgen::prelude::*;
use crate::{nuts::nut01::JsPublicKey, nuts::nut02::JsId, types::JsAmount, types::JsSecret};
#[wasm_bindgen(js_name = Token)]
pub struct JsProof {
inner: Proof,
}
impl Deref for JsProof {
type Target = Proof;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Proof> for JsProof {
fn from(inner: Proof) -> JsProof {
JsProof { inner }
}
}
#[wasm_bindgen(js_class = Proof)]
impl JsProof {
#[wasm_bindgen(constructor)]
pub fn new(amount: JsAmount, secret: JsSecret, c: JsPublicKey, id: Option<JsId>) -> JsProof {
Self {
inner: Proof {
amount: amount.deref().clone(),
secret: secret.deref().clone(),
c: c.deref().clone(),
id: id.map(|i| *i.deref()),
},
}
}
/// Amount
#[wasm_bindgen(getter)]
pub fn amount(&self) -> JsAmount {
self.inner.amount.into()
}
/// Secret
#[wasm_bindgen(getter)]
pub fn secret(&self) -> JsSecret {
self.inner.secret.clone().into()
}
/// C
#[wasm_bindgen(getter)]
pub fn c(&self) -> JsPublicKey {
self.inner.c.clone().into()
}
/// Id
#[wasm_bindgen(getter)]
pub fn id(&self) -> Option<JsId> {
self.inner.id.map(|id| id.into())
}
}

View File

@@ -0,0 +1,59 @@
use std::{ops::Deref, str::FromStr};
use cashu::{nuts::nut00::wallet::Token, url::UncheckedUrl};
use wasm_bindgen::prelude::*;
use crate::error::{into_err, Result};
#[wasm_bindgen(js_name = Token)]
pub struct JsToken {
inner: Token,
}
impl Deref for JsToken {
type Target = Token;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Token> for JsToken {
fn from(inner: Token) -> JsToken {
JsToken { inner }
}
}
#[wasm_bindgen(js_class = Token)]
impl JsToken {
// TODO: Simply passing a string for proofs is not ideal
#[wasm_bindgen(constructor)]
pub fn new(mint: String, proofs: String, memo: Option<String>) -> Result<JsToken> {
let mint = UncheckedUrl::from_str(&mint).map_err(into_err)?;
let proofs = serde_json::from_str(&proofs).map_err(into_err)?;
Ok(Self {
inner: Token::new(mint, proofs, memo).map_err(into_err)?,
})
}
/// Memo
#[wasm_bindgen(getter)]
pub fn memo(&self) -> Option<String> {
self.inner.memo.clone()
}
/// From String
#[wasm_bindgen(js_name = fromString)]
pub fn from_string(token: String) -> Result<JsToken> {
Ok(JsToken {
inner: Token::from_str(&token).map_err(into_err)?,
})
}
/// As String
#[wasm_bindgen(js_name = asString)]
pub fn as_string(&self) -> Result<String> {
Ok(self.inner.convert_to_string().map_err(into_err)?)
}
// TODO: Getter mint proofs
}

View File

@@ -0,0 +1,47 @@
use std::ops::Deref;
use cashu::nuts::nut01::mint::KeyPair;
use wasm_bindgen::prelude::*;
use super::{JsPublicKey, JsSecretKey};
#[wasm_bindgen(js_name = KeyPair)]
pub struct JsKeyPair {
inner: KeyPair,
}
impl Deref for JsKeyPair {
type Target = KeyPair;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<KeyPair> for JsKeyPair {
fn from(inner: KeyPair) -> JsKeyPair {
JsKeyPair { inner }
}
}
#[wasm_bindgen(js_class = KeyPair)]
impl JsKeyPair {
/// From Hex
#[wasm_bindgen(js_name = fromSecretKey)]
pub fn from_secret_key(secret_key: JsSecretKey) -> JsKeyPair {
Self {
inner: KeyPair::from_secret_key(secret_key.deref().clone()),
}
}
/// Secret Key
#[wasm_bindgen(getter)]
pub fn secret_key(&self) -> JsSecretKey {
self.inner.secret_key.clone().into()
}
/// Public Key
#[wasm_bindgen(getter)]
pub fn public_key(&self) -> JsPublicKey {
self.inner.public_key.clone().into()
}
}

View File

@@ -0,0 +1,54 @@
use std::ops::Deref;
use cashu::nuts::nut01::Keys;
use wasm_bindgen::prelude::*;
use crate::{
error::{into_err, Result},
types::JsAmount,
};
use super::JsPublicKey;
#[wasm_bindgen(js_name = Keys)]
pub struct JsKeys {
inner: Keys,
}
impl Deref for JsKeys {
type Target = Keys;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Keys> for JsKeys {
fn from(inner: Keys) -> JsKeys {
JsKeys { inner }
}
}
#[wasm_bindgen(js_class = Keys)]
impl JsKeys {
/// From Hex
#[wasm_bindgen(constructor)]
pub fn new(keys: String) -> Result<JsKeys> {
let keys = serde_json::from_str(&keys).map_err(into_err)?;
Ok(JsKeys {
inner: Keys::new(keys),
})
}
/// Keys
#[wasm_bindgen(js_name = keys)]
pub fn keys(&self) -> Result<String> {
Ok(serde_json::to_string(&self.inner.keys()).map_err(into_err)?)
}
/// Amount Key
#[wasm_bindgen(js_name = amountKey)]
pub fn amount_key(&self, amount: JsAmount) -> Option<JsPublicKey> {
self.inner.amount_key(*amount.deref()).map(|k| k.into())
}
}

View File

@@ -0,0 +1,9 @@
mod key_pair;
mod keys;
mod public_key;
mod secret_key;
pub use key_pair::JsKeyPair;
pub use keys::JsKeys;
pub use public_key::JsPublicKey;
pub use secret_key::JsSecretKey;

View File

@@ -0,0 +1,41 @@
use std::ops::Deref;
use cashu::nuts::nut01::PublicKey;
use wasm_bindgen::prelude::*;
use crate::error::{into_err, Result};
#[wasm_bindgen(js_name = PublicKey)]
pub struct JsPublicKey {
inner: PublicKey,
}
impl Deref for JsPublicKey {
type Target = PublicKey;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<PublicKey> for JsPublicKey {
fn from(inner: PublicKey) -> JsPublicKey {
JsPublicKey { inner }
}
}
#[wasm_bindgen(js_class = PublicKey)]
impl JsPublicKey {
/// From Hex
#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(hex: String) -> Result<JsPublicKey> {
Ok(Self {
inner: PublicKey::from_hex(hex).map_err(into_err)?,
})
}
/// To Hex
#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.inner.to_hex()
}
}

View File

@@ -0,0 +1,31 @@
use std::ops::Deref;
use cashu::nuts::nut01::SecretKey;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = PublicKey)]
pub struct JsSecretKey {
inner: SecretKey,
}
impl Deref for JsSecretKey {
type Target = SecretKey;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<SecretKey> for JsSecretKey {
fn from(inner: SecretKey) -> JsSecretKey {
JsSecretKey { inner }
}
}
#[wasm_bindgen(js_class = SecretKey)]
impl JsSecretKey {
/// To Hex
#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.inner.to_hex()
}
}

View File

@@ -0,0 +1,41 @@
use std::ops::Deref;
use cashu::nuts::nut02::Id;
use wasm_bindgen::prelude::*;
use crate::error::{into_err, Result};
#[wasm_bindgen(js_name = Id)]
pub struct JsId {
inner: Id,
}
impl Deref for JsId {
type Target = Id;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Id> for JsId {
fn from(inner: Id) -> JsId {
JsId { inner }
}
}
#[wasm_bindgen(js_class = Id)]
impl JsId {
/// Try From Base 64 String
#[wasm_bindgen(js_name = tryFromBase64)]
pub fn try_from_base64(id: String) -> Result<JsId> {
Ok(JsId {
inner: Id::try_from_base64(&id).map_err(into_err)?,
})
}
/// As String
#[wasm_bindgen(js_name = asString)]
pub fn as_string(&self) -> String {
self.inner.to_string()
}
}

View File

@@ -0,0 +1,3 @@
pub mod id;
pub use id::JsId;

View File

@@ -0,0 +1,68 @@
use std::ops::Deref;
use cashu::Amount;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = Amount)]
pub struct JsAmount {
inner: Amount,
}
impl Deref for JsAmount {
type Target = Amount;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Amount> for JsAmount {
fn from(inner: Amount) -> JsAmount {
JsAmount { inner }
}
}
#[wasm_bindgen(js_class = Amount)]
impl JsAmount {
#[wasm_bindgen(constructor)]
pub fn new(sats: u64) -> Self {
Self {
inner: Amount::from_sat(sats),
}
}
/// From Sats
#[wasm_bindgen(js_name = fromSat)]
pub fn from_sat(sats: u64) -> Self {
Self {
inner: Amount::from_sat(sats),
}
}
/// From Msats
#[wasm_bindgen(js_name = fromMSat)]
pub fn from_msat(msats: u64) -> Self {
Self {
inner: Amount::from_msat(msats),
}
}
/// Get as sats
#[wasm_bindgen(js_name = toSat)]
pub fn to_sat(&self) -> u64 {
self.inner.to_sat()
}
/// Get as msats
#[wasm_bindgen(js_name = toMSat)]
pub fn to_msat(&self) -> u64 {
self.inner.to_msat()
}
/// Split amount returns sat vec of sats
// REVIEW: https://github.com/rustwasm/wasm-bindgen/issues/111
#[wasm_bindgen(js_name = split)]
pub fn split(&self) -> Vec<u64> {
let split = self.inner.split();
split.into_iter().map(|a| a.to_sat()).collect()
}
}

View File

@@ -0,0 +1,49 @@
use std::{ops::Deref, str::FromStr};
use cashu::Bolt11Invoice;
use wasm_bindgen::prelude::*;
use crate::error::{into_err, Result};
use crate::types::JsAmount;
#[wasm_bindgen(js_name = Bolt11Invoice)]
pub struct JsBolt11Invoice {
inner: Bolt11Invoice,
}
impl Deref for JsBolt11Invoice {
type Target = Bolt11Invoice;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Bolt11Invoice> for JsBolt11Invoice {
fn from(inner: Bolt11Invoice) -> JsBolt11Invoice {
JsBolt11Invoice { inner }
}
}
#[wasm_bindgen(js_class = Bolt11Invoice)]
impl JsBolt11Invoice {
#[wasm_bindgen(constructor)]
pub fn new(invoice: String) -> Result<JsBolt11Invoice> {
Ok(JsBolt11Invoice {
inner: Bolt11Invoice::from_str(&invoice).map_err(into_err)?,
})
}
/// Amount
#[wasm_bindgen(getter)]
pub fn amount(&self) -> Option<JsAmount> {
self.inner
.amount_milli_satoshis()
.map(|a| JsAmount::from_msat(a))
}
/// Invoice as string
#[wasm_bindgen(js_name = asString)]
pub fn as_string(&self) -> String {
self.inner.to_string()
}
}

View File

@@ -0,0 +1,7 @@
pub mod amount;
pub mod bolt11_invoice;
pub mod secret;
pub use amount::JsAmount;
pub use bolt11_invoice::JsBolt11Invoice;
pub use secret::JsSecret;

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,38 @@
use std::ops::Deref;
use cashu::secret::Secret;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = Secret)]
pub struct JsSecret {
inner: Secret,
}
impl Deref for JsSecret {
type Target = Secret;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<Secret> for JsSecret {
fn from(inner: Secret) -> JsSecret {
JsSecret { inner }
}
}
#[wasm_bindgen(js_class = Secret)]
impl JsSecret {
#[wasm_bindgen(constructor)]
pub fn new() -> JsSecret {
Self {
inner: Secret::new(),
}
}
/// As Bytes
#[wasm_bindgen(js_name = asBytes)]
pub fn as_bytes(&self) -> Vec<u8> {
self.inner.as_bytes().to_vec()
}
}