diff --git a/CHANGELOG.md b/CHANGELOG.md index 796eaa3bc..2b1c554a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ * nostr: add `JsonUtil::try_as_json` method ([Yuki Kishimoto]) * nostr: add `public_key` field to `TagStandard::Event` ([Yuki Kishimoto]) * nostr: add support to `nrelay` NIP-19 entity ([Yuki Kishimoto]) +* nostr: add support to ephemeral gift wrap ([Yuki Kishimoto]) * pool: allow to set event limits per kind ([Yuki Kishimoto]) * pool: log warn when high latency ([Yuki Kishimoto]) * sdk: add support to automatic authentication to relays (NIP-42) ([Yuki Kishimoto]) diff --git a/bindings/nostr-ffi/src/event/kind.rs b/bindings/nostr-ffi/src/event/kind.rs index 50c24b658..d2100258b 100644 --- a/bindings/nostr-ffi/src/event/kind.rs +++ b/bindings/nostr-ffi/src/event/kind.rs @@ -207,6 +207,8 @@ pub enum KindEnum { Seal, /// Gift Wrap (NIP59) GiftWrap, + /// Ephemeral Gift Wrap + EphemeralGiftWrap, /// Private Direct message /// /// @@ -307,6 +309,7 @@ impl From for KindEnum { nostr::Kind::BadgeDefinition => Self::BadgeDefinition, nostr::Kind::Seal => Self::Seal, nostr::Kind::GiftWrap => Self::GiftWrap, + nostr::Kind::EphemeralGiftWrap => Self::EphemeralGiftWrap, nostr::Kind::PrivateDirectMessage => Self::PrivateDirectMessage, nostr::Kind::LongFormTextNote => Self::LongFormTextNote, nostr::Kind::ApplicationSpecificData => Self::ApplicationSpecificData, @@ -385,6 +388,7 @@ impl From for nostr::Kind { KindEnum::BadgeDefinition => Self::BadgeDefinition, KindEnum::Seal => Self::Seal, KindEnum::GiftWrap => Self::GiftWrap, + KindEnum::EphemeralGiftWrap => Self::EphemeralGiftWrap, KindEnum::PrivateDirectMessage => Self::PrivateDirectMessage, KindEnum::LongFormTextNote => Self::LongFormTextNote, KindEnum::ApplicationSpecificData => Self::ApplicationSpecificData, diff --git a/bindings/nostr-ffi/src/nips/nip59.rs b/bindings/nostr-ffi/src/nips/nip59.rs index b4e97c347..d1b588bca 100644 --- a/bindings/nostr-ffi/src/nips/nip59.rs +++ b/bindings/nostr-ffi/src/nips/nip59.rs @@ -19,12 +19,14 @@ pub fn gift_wrap( sender_keys: &Keys, receiver_pubkey: &PublicKey, rumor: &UnsignedEvent, + ephemeral: bool, expiration: Option>, ) -> Result { Ok(EventBuilder::gift_wrap( sender_keys.deref(), receiver_pubkey.deref(), rumor.deref().clone(), + ephemeral, expiration.map(|t| **t), )? .into()) @@ -37,12 +39,16 @@ pub fn gift_wrap( pub fn gift_wrap_from_seal( receiver: &PublicKey, seal: &Event, + ephemeral: bool, expiration: Option>, ) -> Result { - Ok( - EventBuilder::gift_wrap_from_seal(receiver.deref(), seal.deref(), expiration.map(|t| **t))? - .into(), - ) + Ok(EventBuilder::gift_wrap_from_seal( + receiver.deref(), + seal.deref(), + ephemeral, + expiration.map(|t| **t), + )? + .into()) } /// Unwrapped Gift Wrap diff --git a/bindings/nostr-js/src/event/builder.rs b/bindings/nostr-js/src/event/builder.rs index a8ea6898e..b8fd9780d 100644 --- a/bindings/nostr-js/src/event/builder.rs +++ b/bindings/nostr-js/src/event/builder.rs @@ -561,11 +561,13 @@ impl JsEventBuilder { pub fn gift_wrap_from_seal( receiver: &JsPublicKey, seal: &JsEvent, + ephemeral: bool, expiration: Option, ) -> Result { Ok(EventBuilder::gift_wrap_from_seal( receiver.deref(), seal.deref(), + ephemeral, expiration.map(|t| *t), ) .map_err(into_err)? @@ -580,12 +582,14 @@ impl JsEventBuilder { sender_keys: &JsKeys, receiver: &JsPublicKey, rumor: &JsUnsignedEvent, + ephemeral: bool, expiration: Option, ) -> Result { Ok(EventBuilder::gift_wrap( sender_keys.deref(), receiver.deref(), rumor.deref().clone(), + ephemeral, expiration.map(|t| *t), ) .map_err(into_err)? diff --git a/bindings/nostr-sdk-ffi/src/client/mod.rs b/bindings/nostr-sdk-ffi/src/client/mod.rs index 464df0f43..2b080e534 100644 --- a/bindings/nostr-sdk-ffi/src/client/mod.rs +++ b/bindings/nostr-sdk-ffi/src/client/mod.rs @@ -566,10 +566,12 @@ impl Client { /// Gift Wrap /// /// + #[uniffi::method(default(expiration = None))] pub async fn gift_wrap( &self, receiver: &PublicKey, rumor: Arc, + ephemeral: bool, expiration: Option>, ) -> Result<()> { Ok(self @@ -577,6 +579,7 @@ impl Client { .gift_wrap( **receiver, rumor.as_ref().deref().clone(), + ephemeral, expiration.map(|t| **t), ) .await?) diff --git a/bindings/nostr-sdk-js/src/client/mod.rs b/bindings/nostr-sdk-js/src/client/mod.rs index 5b0e1a6c2..df3a8f275 100644 --- a/bindings/nostr-sdk-js/src/client/mod.rs +++ b/bindings/nostr-sdk-js/src/client/mod.rs @@ -687,10 +687,16 @@ impl JsClient { &self, receiver: &JsPublicKey, rumor: &JsEventBuilder, + ephemeral: bool, expiration: Option, ) -> Result<()> { self.inner - .gift_wrap(**receiver, rumor.deref().clone(), expiration.map(|t| *t)) + .gift_wrap( + **receiver, + rumor.deref().clone(), + ephemeral, + expiration.map(|t| *t), + ) .await .map_err(into_err) } diff --git a/bindings/nostr-sdk-js/src/database.rs b/bindings/nostr-sdk-js/src/database.rs index 9c1dc00d4..897e8267d 100644 --- a/bindings/nostr-sdk-js/src/database.rs +++ b/bindings/nostr-sdk-js/src/database.rs @@ -54,6 +54,7 @@ impl JsNostrDatabase { pub async fn save_event(&self, event: &JsEvent) -> Result { self.inner.save_event(event).await.map_err(into_err) } + /// Get list of relays that have seen the [`EventId`] #[wasm_bindgen(js_name = eventSeenOnRelays)] pub async fn event_seen_on_relays( diff --git a/crates/nostr-sdk/src/client/mod.rs b/crates/nostr-sdk/src/client/mod.rs index 3dc9a5337..b92558a4b 100644 --- a/crates/nostr-sdk/src/client/mod.rs +++ b/crates/nostr-sdk/src/client/mod.rs @@ -1240,7 +1240,7 @@ impl Client { S: Into, { let rumor: EventBuilder = EventBuilder::private_msg_rumor(receiver, message, reply_to); - self.gift_wrap(receiver, rumor, None).await + self.gift_wrap(receiver, rumor, false, None).await } /// Repost @@ -1475,6 +1475,7 @@ impl Client { &self, receiver: PublicKey, rumor: EventBuilder, + ephemeral: bool, expiration: Option, ) -> Result<(), Error> { // Compose rumor @@ -1490,7 +1491,8 @@ impl Client { let seal: Event = self.sign_event_builder(seal).await?; // Compose gift wrap - let gift_wrap: Event = EventBuilder::gift_wrap_from_seal(&receiver, &seal, expiration)?; + let gift_wrap: Event = + EventBuilder::gift_wrap_from_seal(&receiver, &seal, ephemeral, expiration)?; // Send event self.send_event(gift_wrap).await?; diff --git a/crates/nostr/src/event/builder.rs b/crates/nostr/src/event/builder.rs index 6dde03ada..d50cfb8be 100644 --- a/crates/nostr/src/event/builder.rs +++ b/crates/nostr/src/event/builder.rs @@ -1319,7 +1319,8 @@ impl EventBuilder { pub fn gift_wrap_from_seal( receiver: &PublicKey, seal: &Event, - expiration: Option, + ephemeral: bool, + expiration: Option, // TODO: remove this and add `EventBuilder::expiration`? ) -> Result { if seal.kind != Kind::Seal { return Err(Error::WrongKind { @@ -1343,7 +1344,13 @@ impl EventBuilder { tags.push(Tag::expiration(timestamp)); } - Self::new(Kind::GiftWrap, content, tags) + let kind: Kind = if ephemeral { + Kind::EphemeralGiftWrap + } else { + Kind::GiftWrap + }; + + Self::new(kind, content, tags) .custom_created_at(Timestamp::tweaked(nip59::RANGE_RANDOM_TIMESTAMP_TWEAK)) .to_event(&keys) } @@ -1357,10 +1364,11 @@ impl EventBuilder { sender_keys: &Keys, receiver: &PublicKey, rumor: UnsignedEvent, - expiration: Option, + ephemeral: bool, + expiration: Option, // TODO: remove this and add `EventBuilder::expiration`? ) -> Result { let seal: Event = Self::seal(sender_keys, receiver, rumor)?.to_event(sender_keys)?; - Self::gift_wrap_from_seal(receiver, &seal, expiration) + Self::gift_wrap_from_seal(receiver, &seal, ephemeral, expiration) } /// GiftWrapped Sealed Direct message diff --git a/crates/nostr/src/event/kind.rs b/crates/nostr/src/event/kind.rs index 5f076a609..46dfed41b 100644 --- a/crates/nostr/src/event/kind.rs +++ b/crates/nostr/src/event/kind.rs @@ -137,6 +137,7 @@ kind_variants! { BadgeDefinition => 30009, "Badge Definition (NIP58)", Seal => 13, "Seal ", GiftWrap => 1059, "Gift Wrap ", + EphemeralGiftWrap => 21059, "Ephemeral Gift Wrap", PrivateDirectMessage => 14, "Private Direct message ", SetStall => 30017, "Set stall (NIP15)", SetProduct => 30018, "Set product (NIP15)", diff --git a/crates/nostr/src/nips/nip59.rs b/crates/nostr/src/nips/nip59.rs index 69752f552..8fcdd2f5d 100644 --- a/crates/nostr/src/nips/nip59.rs +++ b/crates/nostr/src/nips/nip59.rs @@ -107,7 +107,7 @@ impl UnwrappedGift { C: Verification, { // Check event kind - if gift_wrap.kind != Kind::GiftWrap { + if gift_wrap.kind != Kind::GiftWrap && gift_wrap.kind != Kind::EphemeralGiftWrap { return Err(Error::NotGiftWrap); } @@ -161,6 +161,7 @@ mod tests { &sender_keys, &receiver_keys.public_key(), rumor.clone(), + false, None, ) .unwrap();