Skip to content

Commit

Permalink
Dev votes user (#48)
Browse files Browse the repository at this point in the history
* Squashed commits

* First working flow of user voting ( to be deeply tested )

* Typo on cargo.toml removed custom dependency

* Refactoring word vote with rating

* Fixing typos of vote

* Regenerated sqlx-data.json and small cargo fmt fix

---------

Co-authored-by: Lucio Pinese <lucio.pinese@electrolux.com>
  • Loading branch information
arkanoider and Lucio Pinese authored Apr 26, 2023
1 parent d960b9e commit d5f96d3
Show file tree
Hide file tree
Showing 10 changed files with 430 additions and 7 deletions.
45 changes: 45 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'mostro'",
"cargo": {
"args": [
"build",
"--bin=mostro",
"--package=mostro"
],
"filter": {
"name": "mostro",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'mostro'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=mostro",
"--package=mostro"
],
"filter": {
"name": "mostro",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ dotenvy = "0.15.6"
easy-hasher = "2.2.1"
lightning-invoice = "0.22.0"
log = "0.4.17"
nostr-sdk = "0.19.1"
#nostr-sdk = "0.21"
nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", branch = "bitcoin-v0.29" }
pretty_env_logger = "0.4.0"
serde = { version = "1.0.149" }
serde_json = "1.0.89"
Expand All @@ -38,4 +39,4 @@ reqwest = { version = "0.11", features = ["json"] }
mostro-core = "0.2.1"
tokio-cron-scheduler = "*"
tracing = "0.1.37"
tracing-subscriber = "0.3.16"
tracing-subscriber = "0.3.16"
4 changes: 3 additions & 1 deletion migrations/20221222153301_orders.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ CREATE TABLE IF NOT EXISTS orders (
range_parent_id varchar(36),
invoice_held_at integer default 0,
taken_at integer default 0,
created_at integer not null
created_at integer not null,
buyer_sent_rate integer default 0,
seller_sent_rate integer default 0
);
10 changes: 10 additions & 0 deletions sqlx-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
},
"query": "\n UPDATE orders\n SET\n status = ?1,\n amount = ?2,\n event_id = ?3\n WHERE id = ?4\n "
},
"27d872e32619954a76458fc8f68d6a0ecbc4e16b8cc6fa2bbf6c0073e81862b7": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 3
}
},
"query": "\n UPDATE orders\n SET\n buyer_sent_rate = ?1,\n seller_sent_rate = ?2 \n WHERE id = ?3\n "
},
"4465f33fba7d31a7154b9710438d9319d02018e873a54d720cda2f7eb07aece6": {
"describe": {
"columns": [],
Expand Down
12 changes: 10 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod add_invoice;
pub mod cancel;
pub mod fiat_sent;
pub mod order;
pub mod rate_user;
pub mod release;
pub mod take_buy;
pub mod take_sell;
Expand All @@ -10,6 +11,7 @@ use crate::app::add_invoice::add_invoice_action;
use crate::app::cancel::cancel_action;
use crate::app::fiat_sent::fiat_sent_action;
use crate::app::order::order_action;
use crate::app::rate_user::update_user_reputation_action;
use crate::app::release::release_action;
use crate::app::take_buy::take_buy_action;
use crate::app::take_sell::take_sell_action;
Expand Down Expand Up @@ -73,12 +75,18 @@ pub async fn run(
.await?
}
Action::PayInvoice => todo!(),
Action::RateUser => {
update_user_reputation_action(
msg, &event, &my_keys, &client, &pool,
)
.await?;
}
_ => todo!(),
}
}
}
};
}
}
};
}
}
}
Expand Down
242 changes: 242 additions & 0 deletions src/app/rate_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
use crate::messages;
use crate::util::{send_dm, update_user_rating_event};

use anyhow::Result;
use log::{error, info};
use mostro_core::order::Order;
use mostro_core::{Action, Content, Message, Rating};
use nostr_sdk::prelude::*;
use sqlx::{Pool, Sqlite};
use sqlx_crud::Crud;
use std::time::Duration;
use tokio::time::timeout;
use uuid::Uuid;

pub async fn send_relays_requests(client: &Client, filters: Filter) -> Option<Event> {
let relays = client.relays().await;

let relays_requests = relays.len();
let mut requests: Vec<tokio::task::JoinHandle<Option<Event>>> =
Vec::with_capacity(relays_requests);
let mut answers_requests = Vec::with_capacity(relays_requests);

for relay in relays.into_iter() {
info!("Requesting to relay : {}", relay.0.as_str());
// Spawn futures and join them at the end
requests.push(tokio::spawn(requests_relay(
client.clone(),
relay.clone(),
filters.clone(),
)));
}

// Get answers from relay
for req in requests {
let ev = req.await.unwrap();
if ev.is_some() {
answers_requests.push(ev.unwrap())
}
}
if answers_requests.is_empty() {
return None;
};

answers_requests.sort_by(|a, b| a.created_at.cmp(&b.created_at));
Some(answers_requests[0].clone())
}

pub async fn requests_relay(client: Client, relay: (Url, Relay), filters: Filter) -> Option<Event> {
let relrequest = get_nip_33_event(&relay.1, vec![filters.clone()], client);

// Buffer vector
let mut res: Option<Event> = None;

// Using a timeout of 3 seconds to avoid unresponsive relays to block the loop forever.
if let Ok(rx) = timeout(Duration::from_secs(3), relrequest).await {
match rx {
Some(m) => res = Some(m),
None => {
res = None;
info!("No requested events found on relay {}", relay.0.to_string());
}
}
}
res
}

pub async fn get_nip_33_event(
relay: &Relay,
filters: Vec<Filter>,
client: Client,
) -> Option<Event> {
// Subscribe
info!(
"Subscribing for all mostro orders to relay : {}",
relay.url().to_string()
);
let id = SubscriptionId::new(Uuid::new_v4().to_string());
let msg = ClientMessage::new_req(id.clone(), filters.clone());

info!("Message sent : {:?}", msg);

// Send msg to relay
relay.send_msg(msg.clone(), false).await.unwrap();

// Wait notification from relays
let mut notifications = client.notifications();

let mut ev = None;

while let Ok(notification) = notifications.recv().await {
if let RelayPoolNotification::Message(_, msg) = notification {
match msg {
RelayMessage::Event {
subscription_id,
event,
} => {
if subscription_id == id {
ev = Some(event.as_ref().clone());
}
}
RelayMessage::EndOfStoredEvents(subscription_id) => {
if subscription_id == id {
break;
}
}
_ => (),
};
}
}

// Unsubscribe
relay.send_msg(ClientMessage::close(id), false).await.ok()?;

ev
}

pub async fn get_counterpart_reputation(
user: &String,
my_keys: &Keys,
client: &Client,
) -> Option<Rating> {
// Request NIP33 of the counterparts

let filter = Filter::new()
.author(my_keys.public_key().to_string())
.kind(Kind::Custom(30000))
.identifier(user.to_string());
println!("Filter : {:?}", filter);
let event_nip33 = send_relays_requests(client, filter).await;

event_nip33.as_ref()?;

let reputation = Rating::from_json(&event_nip33.unwrap().content).unwrap();

Some(reputation)
}

pub async fn update_user_reputation_action(
msg: Message,
event: &Event,
my_keys: &Keys,
client: &Client,
pool: &Pool<Sqlite>,
) -> Result<()> {
let order_id = msg.order_id.unwrap();
let order = match Order::by_id(pool, order_id).await? {
Some(order) => order,
None => {
error!("FiatSent: Order Id {order_id} not found!");
return Ok(());
}
};

// Get needed info about users
let buyer = order.buyer_pubkey.unwrap();
let seller = order.seller_pubkey.unwrap();
let message_sender = event.pubkey.to_bech32()?;

// TODO: send to user a DM with the error
if order.status != "Success" {
error!("FiatSent: Order Id {order_id} wrong status");
return Ok(());
}
// Get counterpart pubkey
let mut counterpart: String = String::new();
let mut buyer_rating: bool = false;
let mut seller_rating: bool = false;

if message_sender == buyer {
counterpart = seller;
buyer_rating = true
} else if message_sender == seller {
counterpart = buyer;
seller_rating = true
};

// Add a check in case of no counterpart found
// if counterpart.is_none() { return anyhow::Error::new(_) };

// Check if content of Peer is the same of counterpart
let mut rating = 0_f64;

if let Content::Peer(p) = msg.content.unwrap() {
if counterpart != p.pubkey {
let text_message = messages::cant_do();
// We create a Message
let message = Message::new(
0,
Some(order.id),
None,
Action::CantDo,
Some(Content::TextMessage(text_message)),
);
let message = message.as_json()?;
send_dm(client, my_keys, &event.pubkey, message).await?;
}
rating = p.rating.unwrap();
}

// Ask counterpart reputation
let rep = get_counterpart_reputation(&counterpart, my_keys, client).await;
//Here we have to update values of the review of the counterpart
let mut reputation;

if rep.is_none() {
reputation = Rating::new(1.0, rating, rating, rating, rating);
} else {
// Update user reputation
//Going on with calculation
reputation = rep.unwrap();
reputation.total_reviews += 1.0;
if rating > reputation.max_rate {
reputation.max_rate = rating
};
if rating < reputation.min_rate {
reputation.min_rate = rating
};
let new_rating =
reputation.last_rating + (rating - reputation.last_rating) / reputation.total_reviews;
reputation.last_rating = new_rating;
}

// Check if the order is not voted by the message sender and in case update NIP
let order_to_check_ratings = crate::db::find_order_by_id(pool, order.id).await?;
if (seller_rating && !order_to_check_ratings.seller_sent_rate)
|| (buyer_rating && !order_to_check_ratings.buyer_sent_rate)
{
//Update db with vote flags
update_user_rating_event(
&counterpart,
buyer_rating,
seller_rating,
reputation.as_json().unwrap(),
order.id,
my_keys,
client,
pool,
)
.await?;
}
Ok(())
}
13 changes: 12 additions & 1 deletion src/app/release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::db::{self};
use crate::lightning::LndConnector;
use crate::messages;
use crate::util::{connect_nostr, get_keys};
use crate::util::{send_dm, update_order_event};
use crate::util::{rate_counterpart, send_dm, update_order_event};

use anyhow::Result;
use log::{error, info};
Expand Down Expand Up @@ -104,6 +104,17 @@ pub async fn release_action(
update_order_event(&pool, &client, &my_keys, status, &order, None)
.await
.unwrap();

// Adding here voting process...
rate_counterpart(
&client,
&buyer_pubkey,
&seller_pubkey,
&my_keys,
order.as_new_order().clone(),
)
.await
.unwrap();
}
}
}
Expand Down
Loading

0 comments on commit d5f96d3

Please sign in to comment.