Skip to content

Commit

Permalink
feat: nostr transport
Browse files Browse the repository at this point in the history
  • Loading branch information
bennyhodl committed Oct 30, 2024
1 parent 40bfff3 commit 6b7827a
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 172 deletions.
29 changes: 21 additions & 8 deletions ddk/src/nostr/marketplace.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::ops::Deref;

use super::util;
use crate::Storage;
use nostr_rs::Timestamp;
use nostr_sdk::{client::builder::ClientBuilder, RelayPoolNotification};
use nostr_sdk::{client::builder::ClientBuilder, Event, Kind, RelayPoolNotification};
use std::ops::Deref;

/// NIP-88 compliant oracle announcement listener.
///
Expand All @@ -23,7 +21,7 @@ where
}
client.connect().await;
let now = Timestamp::now();
let oracle_filter = util::create_oracle_message_filter(now);
let oracle_filter = super::create_oracle_message_filter(now);

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

Expand All @@ -33,9 +31,7 @@ where
relay_url: _,
subscription_id: _,
event,
} => {
util::handle_oracle_event(storage, *event);
}
} => handle_oracle_event(storage, *event),
RelayPoolNotification::Shutdown => {
tracing::error!("Relay disconnected.")
}
Expand All @@ -45,3 +41,20 @@ where

Ok(())
}

fn handle_oracle_event<S: Deref>(storage: &S, event: Event)
where
S::Target: Storage,
{
match event.kind {
Kind::Custom(89) => {
tracing::info!("Oracle attestation. Saved to storage.")
}
Kind::Custom(88) => {
let announcement = crate::util::oracle_announcement_from_str(&event.content).unwrap();
storage.save_announcement(announcement).unwrap();
tracing::info!("Oracle announcement. Saved to storage.")
}
_ => (),
}
}
17 changes: 14 additions & 3 deletions ddk/src/nostr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
use nostr_rs::Kind;
use nostr_rs::{Filter, Kind, PublicKey as NostrPublicKey, Timestamp};

/// Nostr [dlc_messages::oracle_msgs::OracleAnnouncement] marketplace.
#[cfg(feature = "marketplace")]
pub mod marketplace;
/// Nostr related functions for DLC events.
pub mod util;

pub const DLC_MESSAGE_KIND: Kind = Kind::Custom(8_888);
pub const ORACLE_ANNOUNCMENT_KIND: Kind = Kind::Custom(88);
pub const ORACLE_ATTESTATION_KIND: Kind = Kind::Custom(89);

pub fn create_dlc_message_filter(since: Timestamp, public_key: NostrPublicKey) -> Filter {
Filter::new()
.kind(DLC_MESSAGE_KIND)
.since(since)
.pubkey(public_key)
}

pub fn create_oracle_message_filter(since: Timestamp) -> Filter {
Filter::new()
.kinds([ORACLE_ANNOUNCMENT_KIND, ORACLE_ATTESTATION_KIND])
.since(since)
}
54 changes: 0 additions & 54 deletions ddk/src/nostr/util.rs

This file was deleted.

22 changes: 0 additions & 22 deletions ddk/src/transport/nostr/dlc_handler.rs

This file was deleted.

89 changes: 89 additions & 0 deletions ddk/src/transport/nostr/messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::nostr::{DLC_MESSAGE_KIND, ORACLE_ANNOUNCMENT_KIND, ORACLE_ATTESTATION_KIND};
use dlc_messages::message_handler::read_dlc_message;
use dlc_messages::{Message, WireMessage};
use lightning::ln::wire::Type;
use lightning::util::ser::{Readable, Writeable};
use nostr_rs::nips::nip04;
use nostr_rs::{
Event, EventBuilder, EventId, Filter, Keys, Kind, PublicKey, SecretKey, Tag, Timestamp,
};

pub fn create_dlc_message_filter(since: Timestamp, public_key: PublicKey) -> Filter {
Filter::new()
.kind(DLC_MESSAGE_KIND)
.since(since)
.pubkey(public_key)
}

pub fn create_oracle_message_filter(since: Timestamp) -> Filter {
Filter::new()
.kinds([ORACLE_ANNOUNCMENT_KIND, ORACLE_ATTESTATION_KIND])
.since(since)
}

pub fn parse_dlc_msg_event(event: &Event, secret_key: &SecretKey) -> anyhow::Result<Message> {
let decrypt = nip04::decrypt(secret_key, &event.pubkey, &event.content)?;

let bytes = base64::decode(decrypt)?;

let mut cursor = lightning::io::Cursor::new(bytes);

let msg_type: u16 = Readable::read(&mut cursor).unwrap();

let Some(wire) = read_dlc_message(msg_type, &mut cursor).unwrap() else {
return Err(anyhow::anyhow!("Couldn't read DLC message."));
};

match wire {
WireMessage::Message(msg) => Ok(msg),
WireMessage::SegmentStart(_) | WireMessage::SegmentChunk(_) => {
Err(anyhow::anyhow!("Blah blah, something with a wire"))
}
}
}

pub fn handle_dlc_msg_event(
event: &Event,
secret_key: &SecretKey,
) -> anyhow::Result<(dlc::secp256k1_zkp::PublicKey, Message, Event)> {
if event.kind != Kind::Custom(8_888) {
return Err(anyhow::anyhow!("Event reveived was not DLC Message event."));
}

let message = parse_dlc_msg_event(&event, secret_key)?;

let pubkey = bitcoin::secp256k1::PublicKey::from_slice(
&event
.pubkey
.public_key(nostr_sdk::secp256k1::Parity::Even)
.serialize(),
)
.expect("converting pubkey between crates should not fail");

Ok((pubkey, message, event.clone()))
}

pub fn create_dlc_msg_event(
to: PublicKey,
event_id: Option<EventId>,
msg: Message,
keys: &Keys,
) -> anyhow::Result<Event> {
let mut bytes = msg.type_id().encode();
bytes.extend(msg.encode());

let content = nip04::encrypt(&keys.secret_key().clone(), &to, base64::encode(&bytes))?;

let p_tags = Tag::public_key(keys.public_key);

let e_tags = event_id.map(|e| Tag::event(e));

let tags = [Some(p_tags), e_tags]
.into_iter()
.flatten()
.collect::<Vec<_>>();

let event = EventBuilder::new(DLC_MESSAGE_KIND, content, tags).to_event(&keys)?;

Ok(event)
}
42 changes: 38 additions & 4 deletions ddk/src/transport/nostr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
pub mod dlc_handler;
pub mod relay_handler;
mod messages;
mod relay_handler;

pub use dlc_handler::NostrDlcHandler;
pub use nostr_relay_pool::RelayPoolNotification;
pub use relay_handler::NostrDlc;

use crate::{DlcDevKitDlcManager, Oracle, Storage, Transport};
use bitcoin::secp256k1::PublicKey as BitcoinPublicKey;
use dlc_messages::Message;
use nostr_rs::PublicKey;
use std::sync::Arc;

#[async_trait::async_trait]
impl Transport for NostrDlc {
fn name(&self) -> String {
"nostr".to_string()
}

async fn listen(&self) {
self.listen().await.expect("Did not start nostr listener.");
}

/// Get messages that have not been processed yet.
async fn receive_messages<S: Storage, O: Oracle>(
&self,
manager: Arc<DlcDevKitDlcManager<S, O>>,
) {
self.receive_dlc_messages(manager).await
}
/// Send a message to a specific counterparty.
fn send_message(&self, counterparty: BitcoinPublicKey, message: Message) {
let public_key = PublicKey::from_slice(&counterparty.serialize())
.expect("Should not fail converting nostr key to bitcoin key.");
let _event = messages::create_dlc_msg_event(public_key, None, message, &self.keys);
}
/// Connect to another peer
async fn connect_outbound(&self, _pubkey: BitcoinPublicKey, _host: &str) {
todo!("Connect outbound")
}
}
Loading

0 comments on commit 6b7827a

Please sign in to comment.