Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final code on sending messages #86

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ pub enum Commands {
#[arg(short, long)]
#[clap(default_value_t = 30)]
since: i64,
/// If true, get messages from counterparty, otherwise from Mostro
#[arg(short)]
from_user: bool,
},
/// Send direct message to a user
SendDm {
Expand Down Expand Up @@ -223,9 +226,6 @@ pub enum Commands {
/// Pubkey of the counterpart
#[arg(short, long)]
pubkey: String,
/// Event id of the message to derive the key
#[arg(short, long)]
event_id: String,
},
}

Expand Down Expand Up @@ -305,9 +305,8 @@ pub async fn run() -> Result<()> {

if let Some(cmd) = cli.command {
match &cmd {
Commands::ConversationKey { event_id, pubkey } => {
execute_conversation_key(&my_key, PublicKey::from_str(pubkey)?, &client, event_id)
.await?
Commands::ConversationKey { pubkey } => {
execute_conversation_key(&my_key, PublicKey::from_str(pubkey)?).await?
}
Commands::ListOrders {
status,
Expand All @@ -327,7 +326,9 @@ pub async fn run() -> Result<()> {
Commands::AddInvoice { order_id, invoice } => {
execute_add_invoice(order_id, invoice, &my_key, mostro_key, &client).await?
}
Commands::GetDm { since } => execute_get_dm(since, &my_key, &client).await?,
Commands::GetDm { since, from_user } => {
execute_get_dm(since, &my_key, &client, *from_user).await?
}
Commands::FiatSent { order_id }
| Commands::Release { order_id }
| Commands::Dispute { order_id }
Expand Down
2 changes: 1 addition & 1 deletion src/cli/add_invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub async fn execute_add_invoice(
.as_json()
.unwrap();

send_order_id_cmd(client, my_key, mostro_key, add_invoice_message, true).await?;
send_order_id_cmd(client, my_key, mostro_key, add_invoice_message, true, true).await?;

Ok(())
}
39 changes: 9 additions & 30 deletions src/cli/conversation_key.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,17 @@
use anyhow::Result;
use nip44::v2::ConversationKey;
use nostr_sdk::prelude::*;
use std::str::FromStr;

use crate::util::send_relays_requests;

pub async fn execute_conversation_key(
my_key: &Keys,
receiver: PublicKey,
client: &Client,
event_id: &str,
) -> Result<()> {
let id = EventId::from_str(event_id)?;
let filter = Filter::new().id(id).limit(1);
let mostro_req = send_relays_requests(client, filter).await;
let event = mostro_req.first().unwrap().first().unwrap();
// Derive gift wrap conversation key
let gw_ck = ConversationKey::derive(my_key.secret_key()?, &event.pubkey);
let gw_key = gw_ck.as_bytes();
let mut gw_ck_hex = vec![];
for i in gw_key {
gw_ck_hex.push(format!("{:02x}", i));
}
let gw_ck_hex = gw_ck_hex.join("");
// Derive seal conversation key
let seal_ck = ConversationKey::derive(my_key.secret_key()?, &receiver);
let seal_key = seal_ck.as_bytes();
let mut seal_ck_hex = vec![];
for i in seal_key {
seal_ck_hex.push(format!("{:02x}", i));
pub async fn execute_conversation_key(my_key: &Keys, receiver: PublicKey) -> Result<()> {
// Derive conversation key
let ck = ConversationKey::derive(my_key.secret_key()?, &receiver);
let key = ck.as_bytes();
let mut ck_hex = vec![];
for i in key {
ck_hex.push(format!("{:02x}", i));
}
let seal_ck_hex = seal_ck_hex.join("");
println!("Gift wrap Conversation key: {:?}", gw_ck_hex);
println!("Seal Conversation key: {:?}", seal_ck_hex);
let ck_hex = ck_hex.join("");
println!("Conversation key: {:?}", ck_hex);

Ok(())
}
9 changes: 7 additions & 2 deletions src/cli/get_dm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ use nostr_sdk::prelude::*;

use crate::util::get_direct_messages;

pub async fn execute_get_dm(since: &i64, my_key: &Keys, client: &Client) -> Result<()> {
let dm = get_direct_messages(client, my_key, *since).await;
pub async fn execute_get_dm(
since: &i64,
my_key: &Keys,
client: &Client,
from_user: bool,
) -> Result<()> {
let dm = get_direct_messages(client, my_key, *since, from_user).await;
if dm.is_empty() {
println!();
println!("No new messages");
Expand Down
2 changes: 1 addition & 1 deletion src/cli/new_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,6 @@ pub async fn execute_new_order(
.as_json()
.unwrap();

send_order_id_cmd(client, my_key, mostro_key, message, false).await?;
send_order_id_cmd(client, my_key, mostro_key, message, false, true).await?;
Ok(())
}
2 changes: 1 addition & 1 deletion src/cli/rate_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub async fn execute_rate_user(
.as_json()
.unwrap();

send_order_id_cmd(client, my_key, mostro_key, rate_message, true).await?;
send_order_id_cmd(client, my_key, mostro_key, rate_message, true, true).await?;

std::process::exit(0);
}
2 changes: 1 addition & 1 deletion src/cli/send_dm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub async fn execute_send_dm(
client: &Client,
message: &str,
) -> Result<()> {
send_order_id_cmd(client, my_key, receiver, message.to_string(), true).await?;
send_order_id_cmd(client, my_key, receiver, message.to_string(), true, true).await?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/cli/send_msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub async fn execute_send_msg(
.as_json()
.unwrap();
info!("Sending message: {:#?}", message);
send_order_id_cmd(client, my_key, mostro_key, message, false).await?;
send_order_id_cmd(client, my_key, mostro_key, message, false, true).await?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/cli/take_buy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub async fn execute_take_buy(
.as_json()
.unwrap();

send_order_id_cmd(client, my_key, mostro_key, take_buy_message, true).await?;
send_order_id_cmd(client, my_key, mostro_key, take_buy_message, true, true).await?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/cli/take_dispute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub async fn execute_take_dispute(
.as_json()
.unwrap();

send_order_id_cmd(client, my_key, mostro_key, take_dispute_message, true).await?;
send_order_id_cmd(client, my_key, mostro_key, take_dispute_message, true, true).await?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/cli/take_sell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ pub async fn execute_take_sell(
.as_json()
.unwrap();

send_order_id_cmd(client, my_key, mostro_key, take_sell_message, true).await?;
send_order_id_cmd(client, my_key, mostro_key, take_sell_message, true, true).await?;
Ok(())
}
96 changes: 72 additions & 24 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::nip33::{dispute_from_tags, order_from_tags};
use crate::nip59::{gift_wrap, unwrap_gift_wrap};

use anyhow::{Error, Result};
use base64::engine::general_purpose;
use base64::Engine;
use chrono::DateTime;
use dotenvy::var;
use log::{error, info};
Expand All @@ -14,6 +16,7 @@ use nostr_sdk::prelude::*;
use std::time::Duration;
use tokio::time::timeout;
use uuid::Uuid;
use v2::{decrypt_to_bytes, encrypt_to_bytes, ConversationKey};

pub fn get_keys() -> Result<Keys> {
// nostr private key
Expand All @@ -28,9 +31,26 @@ pub async fn send_dm(
sender_keys: &Keys,
receiver_pubkey: &PublicKey,
content: String,
to_user: bool,
) -> Result<()> {
let pow: u8 = var("POW").unwrap_or('0'.to_string()).parse().unwrap();
let event = gift_wrap(sender_keys, *receiver_pubkey, content, None, pow)?;
let event = if to_user {
// Derive conversation key
let ck = ConversationKey::derive(sender_keys.secret_key()?, receiver_pubkey);
// Encrypt content
let encrypted_content = encrypt_to_bytes(&ck, content)?;
// Encode with base64
let b64decoded_content = general_purpose::STANDARD.encode(encrypted_content);
// Compose builder
EventBuilder::new(
Kind::PrivateDirectMessage,
b64decoded_content,
[Tag::public_key(*receiver_pubkey)],
)
.to_pow_event(sender_keys, pow)?
} else {
gift_wrap(sender_keys, *receiver_pubkey, content, None, pow)?
};

info!("Sending event: {event:#?}");
println!("Sending event Id: {}", event.id());
Expand Down Expand Up @@ -62,18 +82,20 @@ pub async fn connect_nostr() -> Result<Client> {
pub async fn send_order_id_cmd(
client: &Client,
my_key: &Keys,
mostro_pubkey: PublicKey,
receiver_pubkey: PublicKey,
message: String,
wait_for_dm_ans: bool,
to_user: bool,
) -> Result<()> {
// Send dm to mostro pub id
send_dm(client, my_key, &mostro_pubkey, message).await?;
println!("to_user: {to_user}");
// Send dm to receiver pubkey
send_dm(client, my_key, &receiver_pubkey, message, to_user).await?;

let mut notifications = client.notifications();

while let Ok(notification) = notifications.recv().await {
if wait_for_dm_ans {
let dm = get_direct_messages(client, my_key, 1).await;
let dm = get_direct_messages(client, my_key, 1, to_user).await;

for el in dm.iter() {
match Message::from_json(&el.0) {
Expand Down Expand Up @@ -223,6 +245,7 @@ pub async fn get_direct_messages(
client: &Client,
my_key: &Keys,
since: i64,
from_user: bool,
) -> Vec<(String, String, u64)> {
// We use a fake timestamp to thwart time-analysis attacks
let fake_since = 2880;
Expand All @@ -232,10 +255,22 @@ pub async fn get_direct_messages(
.timestamp() as u64;

let fake_timestamp = Timestamp::from(fake_since_time);
let filters = Filter::new()
.kind(Kind::GiftWrap)
.pubkey(my_key.public_key())
.since(fake_timestamp);
let filters = if from_user {
let since_time = chrono::Utc::now()
.checked_sub_signed(chrono::Duration::minutes(since))
.unwrap()
.timestamp() as u64;
let timestamp = Timestamp::from(since_time);
Filter::new()
.kind(Kind::PrivateDirectMessage)
.pubkey(my_key.public_key())
.since(timestamp)
} else {
Filter::new()
.kind(Kind::GiftWrap)
.pubkey(my_key.public_key())
.since(fake_timestamp)
};

info!("Request events with event kind : {:?} ", filters.kinds);

Expand All @@ -252,30 +287,43 @@ pub async fn get_direct_messages(
for dm in dms {
if !id_list.contains(&dm.id()) {
id_list.push(dm.id());
let unwrapped_gift = match unwrap_gift_wrap(Some(my_key), None, None, dm) {
Ok(u) => u,
Err(_) => {
continue;
}
};
let created_at: Timestamp;
let content: String;
if from_user {
let ck = ConversationKey::derive(my_key.secret_key().unwrap(), &dm.pubkey);
let b64decoded_content =
match general_purpose::STANDARD.decode(dm.content.as_bytes()) {
Ok(b64decoded_content) => b64decoded_content,
Err(_) => {
continue;
}
};
// Decrypt
let unencrypted_content = decrypt_to_bytes(&ck, b64decoded_content).unwrap();
content = String::from_utf8(unencrypted_content).expect("Found invalid UTF-8");
created_at = dm.created_at;
} else {
let unwrapped_gift = match unwrap_gift_wrap(Some(my_key), None, None, dm) {
Ok(u) => u,
Err(_) => {
continue;
}
};
content = unwrapped_gift.rumor.content;
created_at = unwrapped_gift.rumor.created_at;
}
// Here we discard messages older than the real since parameter
let since_time = chrono::Utc::now()
.checked_sub_signed(chrono::Duration::minutes(since))
.unwrap()
.timestamp() as u64;
if unwrapped_gift.rumor.created_at.as_u64() < since_time {
if created_at.as_u64() < since_time {
continue;
}
let date =
DateTime::from_timestamp(unwrapped_gift.rumor.created_at.as_u64() as i64, 0);
let date = DateTime::from_timestamp(created_at.as_u64() as i64, 0);

let human_date = date.unwrap().format("%H:%M:%S date - %d/%m/%Y").to_string();

direct_messages.push((
unwrapped_gift.rumor.content,
human_date,
unwrapped_gift.rumor.created_at.as_u64(),
));
direct_messages.push((content, human_date, created_at.as_u64()));
}
}
}
Expand Down
Loading