use alloc::string::String;
use core::fmt;
use bitcoin::secp256k1::{Secp256k1, Verification};
use super::nip44;
use crate::event::unsigned::{self, UnsignedEvent};
use crate::event::{self, Event};
use crate::key::{self, Keys, SecretKey};
#[cfg(feature = "std")]
use crate::SECP256K1;
use crate::{JsonUtil, Kind, PublicKey};
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Key(key::Error),
Event(event::Error),
Unsigned(unsigned::Error),
NIP44(nip44::Error),
NotGiftWrap,
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Key(e) => write!(f, "Key: {e}"),
Self::Event(e) => write!(f, "Event: {e}"),
Self::Unsigned(e) => write!(f, "Unsigned event: {e}"),
Self::NIP44(e) => write!(f, "NIP44: {e}"),
Self::NotGiftWrap => write!(f, "Not Gift Wrap event"),
}
}
}
impl From<key::Error> for Error {
fn from(e: key::Error) -> Self {
Self::Key(e)
}
}
impl From<event::Error> for Error {
fn from(e: event::Error) -> Self {
Self::Event(e)
}
}
impl From<unsigned::Error> for Error {
fn from(e: unsigned::Error) -> Self {
Self::Unsigned(e)
}
}
impl From<nip44::Error> for Error {
fn from(e: nip44::Error) -> Self {
Self::NIP44(e)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnwrappedGift {
pub sender: PublicKey,
pub rumor: UnsignedEvent,
}
impl UnwrappedGift {
#[cfg(feature = "std")]
pub fn from_gift_wrap(receiver_keys: &Keys, gift_wrap: &Event) -> Result<Self, Error> {
Self::from_gift_wrap_with_ctx(&SECP256K1, receiver_keys, gift_wrap)
}
pub fn from_gift_wrap_with_ctx<C>(
secp: &Secp256k1<C>,
receiver_keys: &Keys,
gift_wrap: &Event,
) -> Result<Self, Error>
where
C: Verification,
{
if gift_wrap.kind != Kind::GiftWrap {
return Err(Error::NotGiftWrap);
}
let secret_key: &SecretKey = receiver_keys.secret_key()?;
let seal: String = nip44::decrypt(secret_key, gift_wrap.author_ref(), gift_wrap.content())?;
let seal: Event = Event::from_json(seal)?;
seal.verify_with_ctx(secp)?;
let rumor: String = nip44::decrypt(secret_key, seal.author_ref(), seal.content())?;
Ok(UnwrappedGift {
sender: seal.author(),
rumor: UnsignedEvent::from_json(rumor)?,
})
}
}
#[cfg(feature = "std")]
pub fn extract_rumor(receiver_keys: &Keys, gift_wrap: &Event) -> Result<UnwrappedGift, Error> {
UnwrappedGift::from_gift_wrap(receiver_keys, gift_wrap)
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use core::str::FromStr;
use super::*;
use crate::EventBuilder;
#[test]
fn test_extract_rumor() {
let sender_keys = Keys::new(
SecretKey::from_str("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")
.unwrap(),
);
let receiver_keys = Keys::new(
SecretKey::from_str("7b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")
.unwrap(),
);
let rumor: UnsignedEvent =
EventBuilder::text_note("Test", []).to_unsigned_event(sender_keys.public_key());
let event: Event = EventBuilder::gift_wrap(
&sender_keys,
&receiver_keys.public_key(),
rumor.clone(),
None,
)
.unwrap();
assert_eq!(
extract_rumor(&receiver_keys, &event).unwrap(),
UnwrappedGift {
sender: sender_keys.public_key(),
rumor,
}
);
assert!(extract_rumor(&sender_keys, &event).is_err());
let event: Event = EventBuilder::text_note("", [])
.to_event(&sender_keys)
.unwrap();
assert_eq!(
extract_rumor(&receiver_keys, &event).unwrap_err(),
Error::NotGiftWrap
);
}
}