Skip to content

Commit

Permalink
Merge pull request #1040 from MutinyWallet/pub-priv-zaps
Browse files Browse the repository at this point in the history
Add public and private zaps
  • Loading branch information
TonyGiorgio authored Mar 18, 2024
2 parents 9fc2261 + 777d9c1 commit 1974c3a
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 45 deletions.
42 changes: 26 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions mutiny-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ futures-util = { version = "0.3", default-features = false }
reqwest = { version = "0.11", default-features = false, features = ["multipart", "json"] }
async-trait = "0.1.68"
url = { version = "2.3.1", features = ["serde"] }
nostr = { version = "0.28.1", default-features = false, features = ["nip04", "nip05", "nip47", "nip57"] }
nostr-sdk = { version = "0.28.0", default-features = false, features = ["nip04", "nip05", "nip47", "nip57"] }
nostr = { version = "0.29.0", default-features = false, features = ["nip04", "nip05", "nip47", "nip57"] }
nostr-sdk = { version = "0.29.0", default-features = false, features = ["nip04", "nip05", "nip47", "nip57"] }
cbc = { version = "0.1", features = ["alloc"] }
aes = { version = "0.8" }
jwt-compact = { version = "0.8.0-beta.1", features = ["es256k"] }
Expand Down Expand Up @@ -84,8 +84,8 @@ gloo-net = { version = "0.4.0" }
instant = { version = "0.1", features = ["wasm-bindgen"] }
getrandom = { version = "0.2", features = ["js"] }
# add nip07 feature for wasm32
nostr = { version = "0.28.1", default-features = false, features = ["nip04", "nip05", "nip07", "nip47", "nip57"] }
nostr-sdk = { version = "0.28.0", default-features = false, features = ["nip04", "nip05", "nip07", "nip47", "nip57"] }
nostr = { version = "0.29.0", default-features = false, features = ["nip04", "nip05", "nip07", "nip47", "nip57"] }
nostr-sdk = { version = "0.29.0", default-features = false, features = ["nip04", "nip05", "nip07", "nip47", "nip57"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", features = ["rt"] }
Expand Down
6 changes: 6 additions & 0 deletions mutiny-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,12 @@ impl From<nostr::event::builder::Error> for MutinyError {
}
}

impl From<nostr_sdk::signer::Error> for MutinyError {
fn from(_e: nostr_sdk::signer::Error) -> Self {
Self::NostrError
}
}

impl From<payjoin::send::CreateRequestError> for MutinyError {
fn from(_e: payjoin::send::CreateRequestError) -> Self {
Self::PayjoinCreateRequest
Expand Down
102 changes: 88 additions & 14 deletions mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,12 @@ use crate::{
};
use crate::{nostr::NostrManager, utils::sleep};
use ::nostr::nips::nip57;
#[cfg(target_arch = "wasm32")]
use ::nostr::prelude::rand::rngs::OsRng;
use ::nostr::prelude::ZapRequestData;
use ::nostr::{EventId, JsonUtil, Kind};
use ::nostr::{EventBuilder, EventId, JsonUtil, Kind};
#[cfg(target_arch = "wasm32")]
use ::nostr::{Keys, Tag};
use async_lock::RwLock;
use bdk_chain::ConfirmationTime;
use bip39::Mnemonic;
Expand All @@ -100,7 +104,7 @@ use moksha_core::primitives::{
PostMeltQuoteBolt11Response,
};
use moksha_core::token::TokenV3;
use nostr_sdk::{Client, RelayPoolNotification};
use nostr_sdk::{Client, NostrSigner, RelayPoolNotification};
use reqwest::multipart::{Form, Part};
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand Down Expand Up @@ -987,7 +991,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
.expect("Failed to add relays");
client.connect().await;

client.subscribe(last_filters.clone()).await;
client.subscribe(last_filters.clone(), None).await;

// handle NWC requests
let mut notifications = client.notifications();
Expand Down Expand Up @@ -1015,7 +1019,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
if event.verify().is_ok() {
match event.kind {
Kind::WalletConnectRequest => {
match nostr.handle_nwc_request(event, &self_clone).await {
match nostr.handle_nwc_request(*event, &self_clone).await {
Ok(Some(event)) => {
if let Err(e) = client.send_event(event).await {
log_warn!(logger, "Error sending NWC event: {e}");
Expand All @@ -1028,7 +1032,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
}
}
Kind::EncryptedDirectMessage => {
if let Err(e) = nostr.handle_direct_message(event, &self_clone).await {
if let Err(e) = nostr.handle_direct_message(*event, &self_clone).await {
log_error!(logger, "Error handling dm: {e}");
}
}
Expand All @@ -1053,7 +1057,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
if let Ok(current_filters) = nostr.get_filters() {
if !utils::compare_filters_vec(&current_filters, &last_filters) {
log_debug!(logger, "subscribing to new nwc filters");
client.subscribe(current_filters.clone()).await;
client.subscribe(current_filters.clone(), None).await;
last_filters = current_filters;
}
}
Expand Down Expand Up @@ -2178,6 +2182,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
zap_npub: Option<::nostr::PublicKey>,
mut labels: Vec<String>,
comment: Option<String>,
privacy_level: PrivacyLevel,
) -> Result<MutinyInvoice, MutinyError> {
let response = self.lnurl_client.make_request(&lnurl.url).await?;

Expand All @@ -2186,7 +2191,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
let msats = amount_sats * 1000;

// if user's npub is given, do an anon zap
let (zap_request, comment, privacy_level) = match zap_npub {
let (zap_request, comment) = match zap_npub {
Some(zap_npub) => {
let data = ZapRequestData {
public_key: zap_npub,
Expand All @@ -2200,15 +2205,84 @@ impl<S: MutinyStorage> MutinyWallet<S> {
event_id: None,
event_coordinate: None,
};
let event = nip57::anonymous_zap_request(data)?;

(Some(event.as_json()), None, PrivacyLevel::Anonymous)
let event = match privacy_level {
PrivacyLevel::Public => {
self.nostr
.primary_key
.sign_event_builder(EventBuilder::public_zap_request(data))
.await?
}
PrivacyLevel::Private => {
// if we have access to the keys, use those
// otherwise need to implement ourselves to use with NIP-07
match &self.nostr.primary_key {
NostrSigner::Keys(keys) => {
nip57::private_zap_request(data, keys)?
}
#[cfg(target_arch = "wasm32")]
NostrSigner::NIP07(_) => {
// Generate encryption key
// Since we are not doing deterministically, we will
// not be able to decrypt this ourself in the future.
// Unsure of how to best do this without access to the actual secret.
// Everything is saved locally in Mutiny so not the end of the world,
// however clients like Damus won't detect our own private zaps
// that we sent.
let private_zap_keys: Keys = Keys::generate();

let mut tags: Vec<Tag> =
vec![Tag::public_key(data.public_key)];
if let Some(event_id) = data.event_id {
tags.push(Tag::event(event_id));
}
let msg_builder = EventBuilder::new(
Kind::ZapPrivateMessage,
&data.message,
tags,
);
let msg = self
.nostr
.primary_key
.sign_event_builder(msg_builder)
.await?;
let created_at = msg.created_at;
let msg: String = nip57::encrypt_private_zap_message(
&mut OsRng,
private_zap_keys.secret_key().expect("just generated"),
&data.public_key,
msg.as_json(),
)?;

// Create final zap event
let mut tags: Vec<Tag> = data.into();
tags.push(Tag::Anon { msg: Some(msg) });
let private_zap_keys: Keys = Keys::generate();
EventBuilder::new(Kind::ZapRequest, "", tags)
.custom_created_at(created_at)
.to_event(&private_zap_keys)?
}
}
}
PrivacyLevel::Anonymous => nip57::anonymous_zap_request(data)?,
PrivacyLevel::NotAvailable => {
// a zap npub with the privacy level NotAvailable
// is invalid
return Err(MutinyError::InvalidArgumentsError);
}
};

(Some(event.as_json()), None)
}
None => {
// PrivacyLevel only applicable to zaps, without
// a zap npub we cannot do a zap
if privacy_level != PrivacyLevel::NotAvailable {
return Err(MutinyError::InvalidArgumentsError);
}

(None, comment.filter(|c| !c.is_empty()))
}
None => (
None,
comment.filter(|c| !c.is_empty()),
PrivacyLevel::NotAvailable,
),
};

let invoice = self
Expand Down
2 changes: 1 addition & 1 deletion mutiny-core/src/nostr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ impl<S: MutinyStorage> NostrManager<S> {
.pubkey(secret.public_key())
.event(request_event.id);

client.subscribe(vec![filter]).await;
client.subscribe(vec![filter], None).await;

client
.send_event(request_event.clone())
Expand Down
22 changes: 16 additions & 6 deletions mutiny-core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,18 +231,28 @@ pub fn compare_filters_vec(a: &[Filter], b: &[Filter]) -> bool {
}

// compare that we have all the same kinds
let agg_kinds_a: HashSet<Kind> = a.iter().flat_map(|i| i.kinds.clone()).collect();
let agg_kinds_b: HashSet<Kind> = b.iter().flat_map(|i| i.kinds.clone()).collect();
let agg_kinds_a: HashSet<Kind> = a
.iter()
.flat_map(|i| i.kinds.clone().unwrap_or_default())
.collect();
let agg_kinds_b: HashSet<Kind> = b
.iter()
.flat_map(|i| i.kinds.clone().unwrap_or_default())
.collect();

if agg_kinds_a != agg_kinds_b {
return false;
}

// compare same authors
let agg_authors_a: HashSet<nostr::PublicKey> =
a.iter().flat_map(|i| i.authors.clone()).collect();
let agg_authors_b: HashSet<nostr::PublicKey> =
b.iter().flat_map(|i| i.authors.clone()).collect();
let agg_authors_a: HashSet<nostr::PublicKey> = a
.iter()
.flat_map(|i| i.authors.clone().unwrap_or_default())
.collect();
let agg_authors_b: HashSet<nostr::PublicKey> = b
.iter()
.flat_map(|i| i.authors.clone().unwrap_or_default())
.collect();

if agg_authors_a != agg_authors_b {
return false;
Expand Down
4 changes: 2 additions & 2 deletions mutiny-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ lightning-invoice = { version = "0.29.0" }
thiserror = "1.0"
instant = { version = "0.1", features = ["wasm-bindgen"] }
lnurl-rs = { version = "0.4.1", default-features = false }
nostr = { version = "0.28.1", default-features = false, features = ["nip04", "nip05", "nip07", "nip47", "nip57"] }
nostr = { version = "0.29.0", default-features = false, features = ["nip04", "nip05", "nip07", "nip47", "nip57"] }
wasm-logger = "0.2.0"
log = "0.4.17"
rexie = "0.5.0"
Expand All @@ -47,7 +47,7 @@ payjoin = { version = "0.13.0", features = ["send", "base64"] }
fedimint-core = { git = "https://github.com/fedimint/fedimint", rev = "5ade2536015a12a7e003a42b159ccc4a431e1a32" }
moksha-core = { git = "https://github.com/ngutech21/moksha", rev = "18d99977965662d46ccec29fecdb0ce493745917" }

bitcoin-waila = { git = "https://github.com/mutinywallet/bitcoin-waila", rev = "b8b6a4d709e438fbadeb16bdf0c577c59be4a7f2" }
bitcoin-waila = { git = "https://github.com/mutinywallet/bitcoin-waila", rev = "311f8efcb5da9d351dd3445a4236f5f743605aa9" }

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
Expand Down
Loading

0 comments on commit 1974c3a

Please sign in to comment.