diff --git a/crates/nostr/Cargo.toml b/crates/nostr/Cargo.toml index a012648bd..9d0931b4e 100644 --- a/crates/nostr/Cargo.toml +++ b/crates/nostr/Cargo.toml @@ -25,6 +25,7 @@ nip19 = ["dep:bech32"] nip21 = ["nip19"] nip46 = ["nip04"] nip47 = ["nip04"] +mmr = [] [dependencies] aes = { version = "0.8", optional = true } diff --git a/crates/nostr/src/event/builder.rs b/crates/nostr/src/event/builder.rs index 80d396f5b..ae96de32c 100644 --- a/crates/nostr/src/event/builder.rs +++ b/crates/nostr/src/event/builder.rs @@ -3,14 +3,15 @@ //! Event builder +#[cfg(feature = "mmr")] +use bitcoin_hashes::sha256::Hash; use core::fmt; -#[cfg(not(target_arch = "wasm32"))] -use std::time::Instant; - #[cfg(target_arch = "wasm32")] use instant::Instant; use secp256k1::XOnlyPublicKey; use serde_json::{json, Value}; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; use url::Url; pub use super::kind::Kind; @@ -169,6 +170,49 @@ impl EventBuilder { tags.pop(); } } + + /// Build MMR [`Event`] + #[cfg(feature = "mmr")] + pub fn to_mmr_event( + self, + keys: &Keys, + prev_event_hash: Hash, + prev_mmr_root: Hash, + prev_event_pos: i64, + ) -> Result { + let pubkey: XOnlyPublicKey = keys.public_key(); + Ok(self + .to_unsigned_mmr_event(pubkey, prev_event_hash, prev_mmr_root, prev_event_pos) + .sign(keys)?) + } + + /// Build unsigned MMR [`Event`] + #[cfg(feature = "mmr")] + pub fn to_unsigned_mmr_event( + self, + pubkey: XOnlyPublicKey, + prev_event_id: Hash, + prev_mmr_root: Hash, + prev_event_pos: i64, + ) -> UnsignedEvent { + let mut tags: Vec = self.tags; + tags.push(Tag::Mmr { + prev_event_id, + prev_mmr_root, + prev_event_pos, + }); + let created_at: Timestamp = Timestamp::now(); + let id = EventId::new(&pubkey, created_at, &self.kind, &tags, &self.content); + // TODO verify if valid MMR append operation for vector commitment + UnsignedEvent { + id, + pubkey, + created_at, + kind: self.kind, + tags, + content: self.content, + } + } } impl EventBuilder { diff --git a/crates/nostr/src/event/tag.rs b/crates/nostr/src/event/tag.rs index 5e9448d12..e6bc1bd73 100644 --- a/crates/nostr/src/event/tag.rs +++ b/crates/nostr/src/event/tag.rs @@ -7,6 +7,8 @@ use core::fmt; use core::num::ParseIntError; use core::str::FromStr; +#[cfg(feature = "mmr")] +use bitcoin_hashes::sha256::Hash; use secp256k1::schnorr::Signature; use secp256k1::XOnlyPublicKey; use serde::de::Error as DeserializerError; @@ -242,6 +244,9 @@ pub enum TagKind { Lnurl, /// Name tag Name, + #[cfg(feature = "mmr")] + /// Merkle mountain range + Mmr, /// Custom tag kind Custom(String), } @@ -275,6 +280,8 @@ impl fmt::Display for TagKind { Self::Amount => write!(f, "amount"), Self::Lnurl => write!(f, "lnurl"), Self::Name => write!(f, "name"), + #[cfg(feature = "mmr")] + Self::Mmr => write!(f, "mmr"), Self::Custom(tag) => write!(f, "{tag}"), } } @@ -313,6 +320,8 @@ where "amount" => Self::Amount, "lnurl" => Self::Lnurl, "name" => Self::Name, + #[cfg(feature = "mmr")] + "mmr" => Self::Mmr, tag => Self::Custom(tag.to_string()), } } @@ -370,6 +379,12 @@ pub enum Tag { Lnurl(String), Name(String), PublishedAt(Timestamp), + #[cfg(feature = "mmr")] + Mmr { + prev_event_id: Hash, + prev_mmr_root: Hash, + prev_event_pos: i64, + }, } impl Tag { @@ -420,6 +435,8 @@ impl Tag { Tag::Amount(..) => TagKind::Amount, Tag::Name(..) => TagKind::Name, Tag::Lnurl(..) => TagKind::Lnurl, + #[cfg(feature = "mmr")] + Tag::Mmr { .. } => TagKind::Mmr, } } } @@ -581,6 +598,12 @@ where conditions: Conditions::from_str(&tag[2])?, sig: Signature::from_str(&tag[3])?, }), + #[cfg(feature = "mmr")] + TagKind::Mmr => Ok(Self::Mmr { + prev_event_id: Hash::from_str(tag[1].as_str())?, + prev_mmr_root: Hash::from_str(tag[2].as_str())?, + prev_event_pos: i64::from_str(tag[3].as_str())?, + }), _ => Ok(Self::Generic(tag_kind, tag[1..].to_vec())), } } else { @@ -726,6 +749,19 @@ impl From for Vec { Tag::Lnurl(lnurl) => { vec![TagKind::Lnurl.to_string(), lnurl] } + #[cfg(feature = "mmr")] + Tag::Mmr { + prev_event_id, + prev_mmr_root, + prev_event_pos, + } => { + vec![ + TagKind::Mmr.to_string(), + prev_event_id.to_string(), + prev_mmr_root.to_string(), + prev_event_pos.to_string(), + ] + } } } }