From 3daeb113cfef6fa25fea92a55856e9623b971888 Mon Sep 17 00:00:00 2001 From: nostronaut Date: Fri, 19 May 2023 16:18:01 +0200 Subject: [PATCH 1/2] MMR tag: add merkle mountain range tag --- crates/nostr/src/event/builder.rs | 42 +++++++++++++++++++++++++++++++ crates/nostr/src/event/tag.rs | 28 +++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/crates/nostr/src/event/builder.rs b/crates/nostr/src/event/builder.rs index 80d396f5b..3183452cb 100644 --- a/crates/nostr/src/event/builder.rs +++ b/crates/nostr/src/event/builder.rs @@ -7,6 +7,7 @@ use core::fmt; #[cfg(not(target_arch = "wasm32"))] use std::time::Instant; +use bitcoin_hashes::sha256::Hash; #[cfg(target_arch = "wasm32")] use instant::Instant; use secp256k1::XOnlyPublicKey; @@ -169,6 +170,47 @@ impl EventBuilder { tags.pop(); } } + + /// Build MMR [`Event`] + 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`] + 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..465d2b853 100644 --- a/crates/nostr/src/event/tag.rs +++ b/crates/nostr/src/event/tag.rs @@ -7,6 +7,7 @@ use core::fmt; use core::num::ParseIntError; use core::str::FromStr; +use bitcoin_hashes::sha256::Hash; use secp256k1::schnorr::Signature; use secp256k1::XOnlyPublicKey; use serde::de::Error as DeserializerError; @@ -242,6 +243,8 @@ pub enum TagKind { Lnurl, /// Name tag Name, + /// Merkle mountain range + Mmr, /// Custom tag kind Custom(String), } @@ -275,6 +278,7 @@ impl fmt::Display for TagKind { Self::Amount => write!(f, "amount"), Self::Lnurl => write!(f, "lnurl"), Self::Name => write!(f, "name"), + Self::Mmr => write!(f, "mmr"), Self::Custom(tag) => write!(f, "{tag}"), } } @@ -313,6 +317,7 @@ where "amount" => Self::Amount, "lnurl" => Self::Lnurl, "name" => Self::Name, + "mmr" => Self::Mmr, tag => Self::Custom(tag.to_string()), } } @@ -370,6 +375,11 @@ pub enum Tag { Lnurl(String), Name(String), PublishedAt(Timestamp), + Mmr { + prev_event_id: Hash, + prev_mmr_root: Hash, + prev_event_pos: i64, + }, } impl Tag { @@ -420,6 +430,7 @@ impl Tag { Tag::Amount(..) => TagKind::Amount, Tag::Name(..) => TagKind::Name, Tag::Lnurl(..) => TagKind::Lnurl, + Tag::Mmr { .. } => TagKind::Mmr, } } } @@ -581,6 +592,11 @@ where conditions: Conditions::from_str(&tag[2])?, sig: Signature::from_str(&tag[3])?, }), + 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 +742,18 @@ impl From for Vec { Tag::Lnurl(lnurl) => { vec![TagKind::Lnurl.to_string(), lnurl] } + 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(), + ] + } } } } From 2ebd82a795bb4f240096c23343ce6f97c209cada Mon Sep 17 00:00:00 2001 From: nostronaut Date: Thu, 25 May 2023 13:12:03 +0200 Subject: [PATCH 2/2] mmr: add mmr feature --- crates/nostr/Cargo.toml | 1 + crates/nostr/src/event/builder.rs | 10 ++++++---- crates/nostr/src/event/tag.rs | 8 ++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) 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 3183452cb..ae96de32c 100644 --- a/crates/nostr/src/event/builder.rs +++ b/crates/nostr/src/event/builder.rs @@ -3,15 +3,15 @@ //! Event builder -use core::fmt; -#[cfg(not(target_arch = "wasm32"))] -use std::time::Instant; - +#[cfg(feature = "mmr")] use bitcoin_hashes::sha256::Hash; +use core::fmt; #[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; @@ -172,6 +172,7 @@ impl EventBuilder { } /// Build MMR [`Event`] + #[cfg(feature = "mmr")] pub fn to_mmr_event( self, keys: &Keys, @@ -186,6 +187,7 @@ impl EventBuilder { } /// Build unsigned MMR [`Event`] + #[cfg(feature = "mmr")] pub fn to_unsigned_mmr_event( self, pubkey: XOnlyPublicKey, diff --git a/crates/nostr/src/event/tag.rs b/crates/nostr/src/event/tag.rs index 465d2b853..e6bc1bd73 100644 --- a/crates/nostr/src/event/tag.rs +++ b/crates/nostr/src/event/tag.rs @@ -7,6 +7,7 @@ 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; @@ -243,6 +244,7 @@ pub enum TagKind { Lnurl, /// Name tag Name, + #[cfg(feature = "mmr")] /// Merkle mountain range Mmr, /// Custom tag kind @@ -278,6 +280,7 @@ 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}"), } @@ -317,6 +320,7 @@ where "amount" => Self::Amount, "lnurl" => Self::Lnurl, "name" => Self::Name, + #[cfg(feature = "mmr")] "mmr" => Self::Mmr, tag => Self::Custom(tag.to_string()), } @@ -375,6 +379,7 @@ pub enum Tag { Lnurl(String), Name(String), PublishedAt(Timestamp), + #[cfg(feature = "mmr")] Mmr { prev_event_id: Hash, prev_mmr_root: Hash, @@ -430,6 +435,7 @@ impl Tag { Tag::Amount(..) => TagKind::Amount, Tag::Name(..) => TagKind::Name, Tag::Lnurl(..) => TagKind::Lnurl, + #[cfg(feature = "mmr")] Tag::Mmr { .. } => TagKind::Mmr, } } @@ -592,6 +598,7 @@ 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())?, @@ -742,6 +749,7 @@ impl From for Vec { Tag::Lnurl(lnurl) => { vec![TagKind::Lnurl.to_string(), lnurl] } + #[cfg(feature = "mmr")] Tag::Mmr { prev_event_id, prev_mmr_root,