Skip to content

Commit 09338bb

Browse files
committed
add proof of concept for sending orders and replying to them
1 parent 62acbbe commit 09338bb

File tree

19 files changed

+423
-44
lines changed

19 files changed

+423
-44
lines changed

Cargo.toml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,17 @@ clap = { version = "4.5.49", features = ["derive"] }
1919
config = { version = "0.15.16", default-features = true }
2020
dotenvy = { version = "0.15" }
2121
dirs = {version = "6.0.0"}
22+
futures-util = { version = "0.3.31" }
2223
global_utils = { path = "./crates/global_utils" }
23-
hex = { version = "0.4.3" }
24-
itertools = { version = "0.14.0" }
25-
reqwest = { version = "0.12.23", features = ["blocking", "json"] }
26-
ring = { version = "0.17.14" }
24+
nostr = { version = "0.43.1", features = ["std"] }
25+
nostr-sdk = { version = "0.43.0" }
2726
serde = { version = "1.0.228", features = ["derive"] }
2827
serde_json = { version = "1.0.145" }
29-
sha2 = { version = "0.10.9", features = ["compress"] }
30-
simplicity-lang = { version = "0.5.0" }
31-
simplicityhl = { version = "0.2.0", features = ["serde"] }
32-
simplicityhl-core = { version = "0.1.1" }
33-
state_change_types = { path = "./crates/state_change_types" }
3428
thiserror = { version = "2.0.17" }
29+
tokio = { version = "1.48.0", features = ["macros", "test-util"] }
3530
tracing = { version = "0.1.41" }
3631
tracing-appender = { version = "0.2.3" }
3732
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
38-
nostr = { version = "0.43.1" }
39-
tokio-tungstenite = { version = "0.28.0", features = ["native-tls"] }
40-
futures-util = "0.3.31"
41-
tokio = {version = "1.48.0", features = ["full"] }
42-
33+
url = { version = "2.5.7" }
34+
nostr_relay_connector = { path = "./crates/nostr_relay_connector"}
35+
nostr_relay_processor = { path = "./crates/nostr_relay_processor"}

crates/global_utils/src/logger.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn init_logger() -> LoggerGuard {
1717
.with_writer(std_out_writer)
1818
.with_target(false)
1919
.with_level(true)
20-
.with_filter(EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("TRACE")));
20+
.with_filter(EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("DEBUG")));
2121

2222
let std_err_layer = fmt::layer()
2323
.with_writer(std_err_writer)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "nostr_relay_connector"
3+
version.workspace = true
4+
edition.workspace = true
5+
rust-version.workspace = true
6+
authors.workspace = true
7+
readme.workspace = true
8+
9+
[dependencies]
10+
tokio = { workspace = true, features = ["time"] }
11+
futures-util = { workspace = true }
12+
serde_json = { workspace = true }
13+
anyhow = { workspace = true }
14+
url = { workspace = true }
15+
nostr = { workspace = true }
16+
global_utils = { workspace = true }
17+
nostr-sdk = { workspace = true }
18+
thiserror = { workspace = true }
19+
tracing = { workspace = true }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#[derive(Debug, thiserror::Error)]
2+
pub enum RelayClientError {
3+
#[error("Failed to convert custom url to RelayURL, err: {err_msg}")]
4+
FailedToConvertRelayUrl { err_msg: String },
5+
#[error("An error occurred in Nostr Client, err: {0}")]
6+
NostrClientFailure(#[from] nostr_sdk::client::Error),
7+
#[error("Relay Client requires for operation signature, add key to the Client")]
8+
MissingSigner,
9+
}
10+
11+
pub type Result<T> = std::result::Result<T, RelayClientError>;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod error;
2+
pub mod relay_client;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::error::RelayClientError;
2+
use nostr::prelude::*;
3+
use nostr_sdk::pool::Output;
4+
use nostr_sdk::prelude::Events;
5+
use nostr_sdk::{Client, Relay, SubscribeAutoCloseOptions};
6+
use std::collections::HashMap;
7+
use std::fmt::Debug;
8+
use std::sync::Arc;
9+
use std::time::Duration;
10+
use tracing::instrument;
11+
12+
#[derive(Debug)]
13+
pub struct RelayClient {
14+
client: Client,
15+
timeout: Duration,
16+
}
17+
18+
#[derive(Debug)]
19+
pub struct ClientConfig {
20+
pub timeout: Duration,
21+
}
22+
23+
impl RelayClient {
24+
#[instrument(skip_all, level = "debug", err)]
25+
pub async fn connect(
26+
relay_urls: impl IntoIterator<Item = impl TryIntoUrl>,
27+
keys: Option<impl IntoNostrSigner>,
28+
client_config: ClientConfig,
29+
) -> crate::error::Result<Self> {
30+
tracing::debug!(client_config = ?client_config, "Connecting to Nostr Relay Client(s)");
31+
let client = match keys {
32+
None => Client::default(),
33+
Some(keys) => {
34+
let client = Client::new(keys);
35+
client.automatic_authentication(true);
36+
client
37+
}
38+
};
39+
for url in relay_urls {
40+
let url = url
41+
.try_into_url()
42+
.map_err(|err| RelayClientError::FailedToConvertRelayUrl {
43+
err_msg: format!("{:?}", err),
44+
})?;
45+
client.add_relay(url).await?;
46+
}
47+
client.connect().await;
48+
Ok(Self {
49+
client,
50+
timeout: client_config.timeout,
51+
})
52+
}
53+
54+
#[instrument(skip_all, level = "debug", ret)]
55+
pub async fn req_and_wait(&self, filter: Filter) -> crate::error::Result<Events> {
56+
tracing::debug!(filter = ?filter, "Requesting events with filter");
57+
let events = self.client.fetch_combined_events(filter, self.timeout).await?;
58+
Ok(events)
59+
}
60+
61+
#[instrument(skip_all, level = "debug", ret)]
62+
pub async fn get_signer(&self) -> crate::error::Result<Arc<dyn NostrSigner>> {
63+
if !self.client.has_signer().await {
64+
return Err(RelayClientError::MissingSigner);
65+
}
66+
Ok(self.client.signer().await?)
67+
}
68+
69+
#[instrument(skip_all, level = "debug", ret)]
70+
pub async fn get_relays(&self) -> HashMap<RelayUrl, Relay> {
71+
self.client.relays().await
72+
}
73+
74+
#[instrument(skip_all, level = "debug", ret)]
75+
pub async fn publish_event(&self, event: &Event) -> crate::error::Result<EventId> {
76+
if !self.client.has_signer().await {
77+
return Err(RelayClientError::MissingSigner);
78+
}
79+
let event_id = self.client.send_event(event).await?;
80+
let event_id = Self::handle_relay_output(event_id)?;
81+
Ok(event_id)
82+
}
83+
84+
#[instrument(skip(self), level = "debug")]
85+
pub async fn subscribe(
86+
&self,
87+
filter: Filter,
88+
opts: Option<SubscribeAutoCloseOptions>,
89+
) -> crate::error::Result<SubscriptionId> {
90+
Ok(self.client.subscribe(filter, opts).await?.val)
91+
}
92+
93+
#[instrument(skip(self), level = "debug")]
94+
pub async fn unsubscribe(&self, subscription_id: &SubscriptionId) {
95+
self.client.unsubscribe(subscription_id).await;
96+
}
97+
98+
#[instrument(skip_all, level = "debug", ret)]
99+
pub async fn disconnect(&self) -> crate::error::Result<()> {
100+
self.client.disconnect().await;
101+
Ok(())
102+
}
103+
104+
#[instrument(level = "debug")]
105+
fn handle_relay_output<T: Debug>(output: Output<T>) -> crate::error::Result<T> {
106+
tracing::trace!(output = ?output, "Handling Relay output");
107+
Ok(output.val)
108+
}
109+
}

crates/nostr_relay_processor/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,10 @@ readme.workspace = true
88

99
[dependencies]
1010
anyhow = { workspace = true }
11+
tokio = { workspace = true }
12+
global_utils = { workspace = true }
13+
nostr-sdk = { workspace = true }
14+
nostr = { workspace = true }
15+
nostr_relay_connector = { workspace = true }
16+
tracing = { workspace = true }
17+
thiserror = { workspace = true }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use nostr_relay_connector::error::RelayClientError;
2+
3+
#[derive(thiserror::Error, Debug)]
4+
pub enum RelayProcessorError {
5+
#[error(transparent)]
6+
RelayClient(#[from] RelayClientError),
7+
}
8+
9+
pub type Result<T> = std::result::Result<T, RelayProcessorError>;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub(crate) mod list_orders;
2+
pub(crate) mod order_replies;
23
pub(crate) mod place_order;
34
pub(crate) mod reply_order;
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
1-
#[warn(unused)]
2-
pub fn handle() -> anyhow::Result<()> {
3-
Ok(())
1+
use crate::types::{CustomKind, MakerOrderKind};
2+
use nostr::{Filter, NostrSigner};
3+
use nostr_relay_connector::relay_client::RelayClient;
4+
use nostr_sdk::prelude::Events;
5+
use std::collections::{BTreeMap, BTreeSet};
6+
7+
pub async fn handle(client: &RelayClient) -> anyhow::Result<Events> {
8+
let events = client
9+
.req_and_wait(Filter {
10+
ids: None,
11+
authors: Some(BTreeSet::from([client.get_signer().await?.get_public_key().await?])),
12+
kinds: Some(BTreeSet::from([MakerOrderKind::get_kind()])),
13+
search: None,
14+
since: None,
15+
until: None,
16+
limit: None,
17+
generic_tags: BTreeMap::default(),
18+
})
19+
.await?;
20+
Ok(events)
421
}

0 commit comments

Comments
 (0)