1use async_trait::async_trait;
2use nip_55::json_rpc::{
3 JsonRpcError, JsonRpcErrorCode, JsonRpcId, JsonRpcRequest, JsonRpcResponseData,
4 JsonRpcServerHandler, JsonRpcStructuredValue,
5};
6pub use nip_55::UdsClientError;
7use nip_55::{Nip55Client, Nip55Server};
8use nostr_sdk::{Event, Keys, Kind, PublicKey, UnsignedEvent};
9use serde::{Deserialize, Serialize};
10use serde_json::json;
11use std::sync::Arc;
12
13const NIP70_UDS_ADDRESS: &str = "/tmp/nip-70.sock";
14
15const METHOD_NAME_SIGN_EVENT: &str = "signEvent";
16
17#[derive(Clone, Debug, PartialEq)]
19pub enum Nip70ServerError {
20 Rejected,
23
24 InternalError,
26
27 MethodNotFound,
29}
30
31impl Nip70ServerError {
32 fn to_json_rpc_error(&self) -> JsonRpcError {
33 match self {
34 Nip70ServerError::Rejected => {
35 JsonRpcError::new(JsonRpcErrorCode::Custom(1), "Rejected".to_string(), None)
36 }
37 Nip70ServerError::InternalError => JsonRpcError::new(
38 JsonRpcErrorCode::InternalError,
39 "Internal error".to_string(),
40 None,
41 ),
42 Nip70ServerError::MethodNotFound => JsonRpcError::new(
43 JsonRpcErrorCode::MethodNotFound,
44 "Method not found".to_string(),
45 None,
46 ),
47 }
48 }
49}
50
51#[async_trait]
54pub trait Nip70: Send + Sync {
55 async fn sign_event(&self, event: UnsignedEvent) -> Result<Event, Nip70ServerError>;
57}
58
59pub struct Nip70Server {
61 nip55_server: Nip55Server,
62}
63
64impl Nip70Server {
65 pub fn start(nip70: Arc<dyn Nip70>, server_keypair: Keys) -> std::io::Result<Self> {
67 Self::start_internal(nip70, NIP70_UDS_ADDRESS.to_string(), server_keypair)
68 }
69
70 fn start_internal(
71 nip70: Arc<dyn Nip70>,
72 uds_address: String,
73 server_keypair: Keys,
74 ) -> std::io::Result<Self> {
75 Ok(Self {
76 nip55_server: Nip55Server::start(
77 uds_address,
78 server_keypair,
79 Box::from(Nip70ServerHandler { nip70 }),
80 )?,
81 })
82 }
83
84 pub fn stop(self) {
86 self.nip55_server.stop();
87 }
88}
89
90struct Nip70ServerHandler {
91 nip70: Arc<dyn Nip70>,
92}
93
94#[async_trait::async_trait]
95impl JsonRpcServerHandler for Nip70ServerHandler {
96 async fn handle_batch_request(
97 &self,
98 requests: Vec<JsonRpcRequest>,
99 ) -> Vec<JsonRpcResponseData> {
100 let mut responses = Vec::new();
101
102 for request in requests {
103 let parsed_request = match Nip70Request::from_json_rpc_request(&request) {
104 Ok(request) => request,
105 Err(error) => {
106 responses.push(JsonRpcResponseData::Error {
107 error: error.to_json_rpc_error(),
108 });
109 continue;
110 }
111 };
112
113 let response_or = match parsed_request {
114 Nip70Request::SignEvent(event) => match self.nip70.sign_event(event).await {
116 Ok(event) => Ok(Nip70Response::SignEvent(event)),
117 Err(err) => Err(err),
118 },
119 };
120
121 responses.push(match response_or {
122 Ok(response) => response.to_json_rpc_response_data(),
123 Err(err) => JsonRpcResponseData::Error {
124 error: err.to_json_rpc_error(),
125 },
126 });
127 }
128
129 responses
130 }
131}
132
133#[derive(Clone, Debug, PartialEq)]
135pub enum Nip70ClientError {
136 UdsClientError(UdsClientError),
137 ProtocolError,
138 ServerError(Nip70ServerError),
139}
140
141impl Nip70ClientError {
142 fn from_json_rpc_error(error: &JsonRpcError) -> Self {
143 match error.code() {
144 JsonRpcErrorCode::Custom(1) => Self::ServerError(Nip70ServerError::Rejected),
145 JsonRpcErrorCode::InternalError => Self::ServerError(Nip70ServerError::InternalError),
146 JsonRpcErrorCode::MethodNotFound => Self::ServerError(Nip70ServerError::MethodNotFound),
147 _ => Self::ProtocolError,
148 }
149 }
150}
151
152#[derive(Clone)]
154pub struct Nip70Client {
155 transport: Nip55Client,
156}
157
158impl Default for Nip70Client {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl Nip70Client {
165 pub fn new() -> Self {
166 Self::new_internal(NIP70_UDS_ADDRESS.to_string())
167 }
168
169 fn new_internal(uds_address: String) -> Self {
170 Self {
171 transport: Nip55Client::new(uds_address),
172 }
173 }
174
175 pub async fn sign_event(
177 &self,
178 event: UnsignedEvent,
179 server_pubkey: PublicKey,
180 ) -> Result<Event, Nip70ClientError> {
181 self.send_request(Nip70Request::SignEvent(event), server_pubkey)
182 .await
183 .map(|response| match response {
184 Nip70Response::SignEvent(event) => Ok(event),
185 })?
186 }
187
188 async fn send_request(
189 &self,
190 request: Nip70Request,
191 server_pubkey: PublicKey,
192 ) -> Result<Nip70Response, Nip70ClientError> {
193 let json_rpc_request = request.to_json_rpc_request(JsonRpcId::Null);
195 let json_rpc_response = self
196 .transport
197 .send_request(Kind::NostrConnect, &json_rpc_request, server_pubkey)
198 .await
199 .map_err(Nip70ClientError::UdsClientError)?;
200 Nip70Response::from_json_rpc_response_data(json_rpc_response.data())
201 }
202}
203
204enum Nip70Request {
205 SignEvent(UnsignedEvent),
206}
207
208impl Nip70Request {
209 fn get_method_name(&self) -> &str {
210 match self {
211 Nip70Request::SignEvent(_) => METHOD_NAME_SIGN_EVENT,
212 }
213 }
214
215 fn get_params(&self) -> Option<JsonRpcStructuredValue> {
216 match self {
217 Nip70Request::SignEvent(event) => Some(JsonRpcStructuredValue::Object(
218 json!(event)
221 .as_object()
222 .expect("Failed to convert event to object")
223 .clone(),
224 )),
225 }
226 }
227
228 fn to_json_rpc_request(&self, request_id: JsonRpcId) -> JsonRpcRequest {
229 JsonRpcRequest::new(
230 self.get_method_name().to_string(),
231 self.get_params(),
232 request_id,
233 )
234 }
235
236 fn from_json_rpc_request(request: &JsonRpcRequest) -> Result<Self, Nip70ServerError> {
237 match request.method() {
238 METHOD_NAME_SIGN_EVENT => Ok(Nip70Request::SignEvent(
239 if let Ok(value) =
240 serde_json::from_value(match request.params().map(|v| v.clone().into_value()) {
241 Some(value) => value,
242 None => return Err(Nip70ServerError::InternalError),
243 })
244 {
245 value
246 } else {
247 return Err(Nip70ServerError::InternalError);
248 },
249 )),
250 _ => Err(Nip70ServerError::MethodNotFound),
251 }
252 }
253}
254
255#[derive(Serialize, Deserialize)]
256#[serde(untagged)]
257enum Nip70Response {
258 SignEvent(Event),
259}
260
261impl Nip70Response {
262 fn to_json_rpc_response_data(&self) -> JsonRpcResponseData {
263 JsonRpcResponseData::Success {
264 result: serde_json::to_value(self).unwrap(),
265 }
266 }
267
268 fn from_json_rpc_response_data(
269 response: &JsonRpcResponseData,
270 ) -> Result<Self, Nip70ClientError> {
271 let result = match response {
272 JsonRpcResponseData::Success { result } => result,
273 JsonRpcResponseData::Error { error } => {
274 return Err(Nip70ClientError::from_json_rpc_error(error))
275 }
276 };
277
278 if let Ok(value) = serde_json::from_value(result.clone()) {
279 Ok(value)
280 } else {
281 Err(Nip70ClientError::ProtocolError)
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 use nostr_sdk::{EventId, Keys, Kind, Timestamp};
291 use std::{sync::Mutex, time::Duration};
292
293 struct TestNip70Implementation {
294 keys: Keys,
295 reject_all_requests: bool,
296 }
297
298 impl TestNip70Implementation {
299 fn new_with_generated_keys() -> (Self, Keys) {
300 let keys = Keys::generate();
301
302 (
303 Self {
304 keys: keys.clone(),
305 reject_all_requests: false,
306 },
307 keys,
308 )
309 }
310
311 fn new_rejecting_all_requests() -> (Self, Keys) {
312 let keys = Keys::generate();
313
314 (
315 Self {
316 keys: keys.clone(),
317 reject_all_requests: true,
318 },
319 keys,
320 )
321 }
322 }
323
324 #[async_trait]
325 impl Nip70 for TestNip70Implementation {
326 async fn sign_event(&self, event: UnsignedEvent) -> Result<Event, Nip70ServerError> {
327 if self.reject_all_requests {
328 return Err(Nip70ServerError::Rejected);
329 }
330
331 event
332 .sign(&self.keys)
333 .map_err(|_| Nip70ServerError::InternalError)
334 }
335 }
336
337 lazy_static::lazy_static! {
338 static ref UDS_ADDRESS_COUNTER: Arc<Mutex<i32>> = Arc::new(Mutex::new(0));
339 }
340
341 fn get_free_uds_address() -> String {
342 let mut counter = UDS_ADDRESS_COUNTER.lock().unwrap();
343 let uds_address = format!("/tmp/nip70-{}.sock", *counter);
344 *counter += 1;
345 uds_address
346 }
347
348 fn get_nip70_server_and_client_test_pair(
349 nip70: Arc<dyn Nip70>,
350 ) -> (Nip70Server, Nip70Client, Keys) {
351 let uds_address = get_free_uds_address();
352 let server_keypair = Keys::generate();
353 let server =
354 Nip70Server::start_internal(nip70, uds_address.clone(), server_keypair.clone())
355 .unwrap();
356 let client = Nip70Client::new_internal(uds_address);
357 (server, client, server_keypair)
358 }
359
360 #[tokio::test]
361 async fn sign_event_over_uds() {
362 let (nip70, keys) = TestNip70Implementation::new_with_generated_keys();
363 let (server, client, server_keypair) =
364 get_nip70_server_and_client_test_pair(Arc::from(nip70));
365
366 let pubkey = keys.public_key();
367 let created_at = Timestamp::now();
368 let kind = Kind::TextNote;
369 let tags = vec![];
370 let content = String::from("Hello, world!");
371 let unsigned_event = UnsignedEvent {
372 id: Some(EventId::new(&pubkey, created_at, &kind, &tags, &content)),
373 pubkey,
374 created_at,
375 kind,
376 tags,
377 content,
378 };
379
380 let event = client
381 .sign_event(unsigned_event, server_keypair.public_key())
382 .await
383 .unwrap();
384
385 assert!(event.verify().is_ok());
386
387 server.stop();
388 }
389
390 #[tokio::test]
391 async fn sign_large_event_over_uds() {
392 let (nip70, keys) = TestNip70Implementation::new_with_generated_keys();
393 let (server, client, server_keypair) =
394 get_nip70_server_and_client_test_pair(Arc::from(nip70));
395
396 let pubkey = keys.public_key();
397 let created_at = Timestamp::now();
398 let kind = Kind::TextNote;
399 let tags = vec![];
400 let content: String = std::iter::repeat('a').take((2 as usize).pow(20)).collect();
401 let unsigned_event = UnsignedEvent {
402 id: Some(EventId::new(&pubkey, created_at, &kind, &tags, &content)),
403 pubkey,
404 created_at,
405 kind,
406 tags,
407 content,
408 };
409
410 let event = client
411 .sign_event(unsigned_event, server_keypair.public_key())
412 .await
413 .unwrap();
414
415 assert!(event.verify().is_ok());
416
417 server.stop();
418 }
419
420 #[test]
421 #[should_panic(expected = "must be called from the context of a Tokio 1.x runtime")]
422 fn run_server_without_async_runtime() {
423 let uds_address = get_free_uds_address();
424 let (nip70, keys) = TestNip70Implementation::new_with_generated_keys();
425 Nip70Server::start_internal(Arc::from(nip70), uds_address.clone(), keys).unwrap();
426 }
427
428 #[tokio::test]
429 async fn sign_event_over_uds_load() {
430 let (nip70, keys) = TestNip70Implementation::new_with_generated_keys();
431 let (server, client, server_keypair) =
432 get_nip70_server_and_client_test_pair(Arc::from(nip70));
433
434 let pubkey = keys.public_key();
435
436 let mut client_handles = Vec::new();
437 for i in 0..128 {
438 let client = client.clone();
439 let server_keypair = server_keypair.clone();
440 let handle = tokio::spawn(async move {
441 for j in 0..20 {
442 let created_at = Timestamp::now();
443 let kind = Kind::TextNote;
444 let tags = vec![];
445 let content = format!("Message {} from thread {}.", j, i);
446 let unsigned_event = UnsignedEvent {
447 id: Some(EventId::new(&pubkey, created_at, &kind, &tags, &content)),
448 pubkey,
449 created_at,
450 kind,
451 tags,
452 content,
453 };
454
455 let event = client
456 .sign_event(unsigned_event.clone(), server_keypair.public_key())
457 .await
458 .unwrap();
459
460 assert!(event.verify().is_ok());
461 assert_eq!(Some(event.id), unsigned_event.id);
462
463 tokio::time::sleep(Duration::from_millis(50)).await;
465 }
466 });
467 client_handles.push(handle);
468 }
469
470 for handle in client_handles {
471 handle.await.unwrap();
472 }
473
474 server.stop();
475 }
476
477 #[tokio::test]
478 async fn make_rpc_with_no_server() {
479 let client = Nip70Client::new_internal(get_free_uds_address());
480 let keys = Keys::generate();
481
482 let pubkey = keys.public_key();
483 let created_at = Timestamp::now();
484 let kind = Kind::TextNote;
485 let tags = vec![];
486 let content = String::from("Hello, world!");
487 let unsigned_event = UnsignedEvent {
488 id: Some(EventId::new(&pubkey, created_at, &kind, &tags, &content)),
489 pubkey,
490 created_at,
491 kind,
492 tags,
493 content,
494 };
495
496 assert_eq!(
497 client.sign_event(unsigned_event, pubkey).await,
498 Err(Nip70ClientError::UdsClientError(
499 UdsClientError::ServerNotRunning
500 ))
501 );
502 }
503
504 #[tokio::test]
505 async fn make_rpc_with_rejected_request() {
506 let (nip70, keys) = TestNip70Implementation::new_rejecting_all_requests();
507 let (server, client, server_keypair) =
508 get_nip70_server_and_client_test_pair(Arc::from(nip70));
509
510 let pubkey = keys.public_key();
511 let created_at = Timestamp::now();
512 let kind = Kind::TextNote;
513 let tags = vec![];
514 let content = String::from("Hello, world!");
515 let unsigned_event = UnsignedEvent {
516 id: Some(EventId::new(&pubkey, created_at, &kind, &tags, &content)),
517 pubkey,
518 created_at,
519 kind,
520 tags,
521 content,
522 };
523
524 assert_eq!(
525 client
526 .sign_event(unsigned_event, server_keypair.public_key())
527 .await,
528 Err(Nip70ClientError::ServerError(Nip70ServerError::Rejected))
529 );
530
531 server.stop();
532 }
533}