radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

commit 429ba5ec8a93a493a29c592383a3d3cad80b9135
parent b705a954a0b6e1dde8cf54f5864cdbaaeeacdc1e
Author: triesap <triesap@radroots.dev>
Date:   Tue,  6 Jan 2026 14:37:28 +0000

nip46: add nip04/nip44 rpc methods

- add nip04 encrypt/decrypt jsonrpc handlers
- add nip44 encrypt/decrypt jsonrpc handlers
- parse pubkeys and map nip46 responses to payloads
- register new nip46 methods alongside existing flow

Diffstat:
Msrc/api/jsonrpc/methods/nip46/mod.rs | 8++++++++
Asrc/api/jsonrpc/methods/nip46/nip04_decrypt.rs | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/api/jsonrpc/methods/nip46/nip04_encrypt.rs | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/api/jsonrpc/methods/nip46/nip44_decrypt.rs | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/api/jsonrpc/methods/nip46/nip44_encrypt.rs | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 306 insertions(+), 0 deletions(-)

diff --git a/src/api/jsonrpc/methods/nip46/mod.rs b/src/api/jsonrpc/methods/nip46/mod.rs @@ -8,6 +8,10 @@ use crate::api::jsonrpc::{MethodRegistry, RpcContext}; pub mod status; pub mod connect; pub mod ping; +pub mod nip04_encrypt; +pub mod nip04_decrypt; +pub mod nip44_encrypt; +pub mod nip44_decrypt; pub mod get_public_key; pub mod sign_event; pub mod session_status; @@ -18,6 +22,10 @@ pub fn module(ctx: RpcContext, registry: MethodRegistry) -> Result<RpcModule<Rpc status::register(&mut m, &registry)?; connect::register(&mut m, &registry)?; ping::register(&mut m, &registry)?; + nip04_encrypt::register(&mut m, &registry)?; + nip04_decrypt::register(&mut m, &registry)?; + nip44_encrypt::register(&mut m, &registry)?; + nip44_decrypt::register(&mut m, &registry)?; get_public_key::register(&mut m, &registry)?; sign_event::register(&mut m, &registry)?; session_status::register(&mut m, &registry)?; diff --git a/src/api/jsonrpc/methods/nip46/nip04_decrypt.rs b/src/api/jsonrpc/methods/nip46/nip04_decrypt.rs @@ -0,0 +1,76 @@ +use anyhow::Result; +use jsonrpsee::server::RpcModule; +use serde::{Deserialize, Serialize}; + +use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError}; +use crate::nip46::client; +use crate::nip46::session::Nip46Session; +use radroots_nostr::prelude::radroots_nostr_parse_pubkey; +use nostr::nips::nip46::{NostrConnectMethod, NostrConnectRequest, ResponseResult}; + +#[derive(Debug, Deserialize)] +struct Nip46Nip04DecryptParams { + session_id: String, + pubkey: String, + ciphertext: String, +} + +#[derive(Clone, Debug, Serialize)] +struct Nip46Nip04DecryptResponse { + plaintext: String, +} + +pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { + registry.track("nip46.nip04_decrypt"); + m.register_async_method("nip46.nip04_decrypt", |params, ctx, _| async move { + let Nip46Nip04DecryptParams { + session_id, + pubkey, + ciphertext, + } = params + .parse() + .map_err(|e| RpcError::InvalidParams(e.to_string()))?; + let session = ctx + .state + .nip46_sessions + .get(&session_id) + .await + .ok_or_else(|| RpcError::InvalidParams("unknown session".to_string()))?; + let plaintext = request_nip04_decrypt(&session, pubkey, ciphertext).await?; + Ok::<Nip46Nip04DecryptResponse, RpcError>(Nip46Nip04DecryptResponse { plaintext }) + })?; + Ok(()) +} + +async fn request_nip04_decrypt( + session: &Nip46Session, + pubkey: String, + ciphertext: String, +) -> Result<String, RpcError> { + let public_key = radroots_nostr_parse_pubkey(&pubkey) + .map_err(|e| RpcError::InvalidParams(format!("invalid pubkey: {e}")))?; + let request = NostrConnectRequest::Nip04Decrypt { + public_key, + ciphertext, + }; + let response = client::request(session, request, "nip04_decrypt").await?; + let response = response + .to_response(NostrConnectMethod::Nip04Decrypt) + .map_err(|e| RpcError::Other(format!("nip46 nip04_decrypt failed: {e}")))?; + + if let Some(error) = response.error { + return Err(RpcError::Other(format!( + "nip46 nip04_decrypt error: {error}" + ))); + } + + match response.result { + Some(ResponseResult::Nip04Decrypt { plaintext }) => Ok(plaintext), + Some(_) => Err(RpcError::Other( + "nip46 nip04_decrypt unexpected response".to_string(), + )), + None => Err(RpcError::Other( + "nip46 nip04_decrypt missing response".to_string(), + )), + } +} diff --git a/src/api/jsonrpc/methods/nip46/nip04_encrypt.rs b/src/api/jsonrpc/methods/nip46/nip04_encrypt.rs @@ -0,0 +1,73 @@ +use anyhow::Result; +use jsonrpsee::server::RpcModule; +use serde::{Deserialize, Serialize}; + +use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError}; +use crate::nip46::client; +use crate::nip46::session::Nip46Session; +use radroots_nostr::prelude::radroots_nostr_parse_pubkey; +use nostr::nips::nip46::{NostrConnectMethod, NostrConnectRequest, ResponseResult}; + +#[derive(Debug, Deserialize)] +struct Nip46Nip04EncryptParams { + session_id: String, + pubkey: String, + text: String, +} + +#[derive(Clone, Debug, Serialize)] +struct Nip46Nip04EncryptResponse { + ciphertext: String, +} + +pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { + registry.track("nip46.nip04_encrypt"); + m.register_async_method("nip46.nip04_encrypt", |params, ctx, _| async move { + let Nip46Nip04EncryptParams { + session_id, + pubkey, + text, + } = params + .parse() + .map_err(|e| RpcError::InvalidParams(e.to_string()))?; + let session = ctx + .state + .nip46_sessions + .get(&session_id) + .await + .ok_or_else(|| RpcError::InvalidParams("unknown session".to_string()))?; + let ciphertext = request_nip04_encrypt(&session, pubkey, text).await?; + Ok::<Nip46Nip04EncryptResponse, RpcError>(Nip46Nip04EncryptResponse { ciphertext }) + })?; + Ok(()) +} + +async fn request_nip04_encrypt( + session: &Nip46Session, + pubkey: String, + text: String, +) -> Result<String, RpcError> { + let public_key = radroots_nostr_parse_pubkey(&pubkey) + .map_err(|e| RpcError::InvalidParams(format!("invalid pubkey: {e}")))?; + let request = NostrConnectRequest::Nip04Encrypt { public_key, text }; + let response = client::request(session, request, "nip04_encrypt").await?; + let response = response + .to_response(NostrConnectMethod::Nip04Encrypt) + .map_err(|e| RpcError::Other(format!("nip46 nip04_encrypt failed: {e}")))?; + + if let Some(error) = response.error { + return Err(RpcError::Other(format!( + "nip46 nip04_encrypt error: {error}" + ))); + } + + match response.result { + Some(ResponseResult::Nip04Encrypt { ciphertext }) => Ok(ciphertext), + Some(_) => Err(RpcError::Other( + "nip46 nip04_encrypt unexpected response".to_string(), + )), + None => Err(RpcError::Other( + "nip46 nip04_encrypt missing response".to_string(), + )), + } +} diff --git a/src/api/jsonrpc/methods/nip46/nip44_decrypt.rs b/src/api/jsonrpc/methods/nip46/nip44_decrypt.rs @@ -0,0 +1,76 @@ +use anyhow::Result; +use jsonrpsee::server::RpcModule; +use serde::{Deserialize, Serialize}; + +use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError}; +use crate::nip46::client; +use crate::nip46::session::Nip46Session; +use radroots_nostr::prelude::radroots_nostr_parse_pubkey; +use nostr::nips::nip46::{NostrConnectMethod, NostrConnectRequest, ResponseResult}; + +#[derive(Debug, Deserialize)] +struct Nip46Nip44DecryptParams { + session_id: String, + pubkey: String, + ciphertext: String, +} + +#[derive(Clone, Debug, Serialize)] +struct Nip46Nip44DecryptResponse { + plaintext: String, +} + +pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { + registry.track("nip46.nip44_decrypt"); + m.register_async_method("nip46.nip44_decrypt", |params, ctx, _| async move { + let Nip46Nip44DecryptParams { + session_id, + pubkey, + ciphertext, + } = params + .parse() + .map_err(|e| RpcError::InvalidParams(e.to_string()))?; + let session = ctx + .state + .nip46_sessions + .get(&session_id) + .await + .ok_or_else(|| RpcError::InvalidParams("unknown session".to_string()))?; + let plaintext = request_nip44_decrypt(&session, pubkey, ciphertext).await?; + Ok::<Nip46Nip44DecryptResponse, RpcError>(Nip46Nip44DecryptResponse { plaintext }) + })?; + Ok(()) +} + +async fn request_nip44_decrypt( + session: &Nip46Session, + pubkey: String, + ciphertext: String, +) -> Result<String, RpcError> { + let public_key = radroots_nostr_parse_pubkey(&pubkey) + .map_err(|e| RpcError::InvalidParams(format!("invalid pubkey: {e}")))?; + let request = NostrConnectRequest::Nip44Decrypt { + public_key, + ciphertext, + }; + let response = client::request(session, request, "nip44_decrypt").await?; + let response = response + .to_response(NostrConnectMethod::Nip44Decrypt) + .map_err(|e| RpcError::Other(format!("nip46 nip44_decrypt failed: {e}")))?; + + if let Some(error) = response.error { + return Err(RpcError::Other(format!( + "nip46 nip44_decrypt error: {error}" + ))); + } + + match response.result { + Some(ResponseResult::Nip44Decrypt { plaintext }) => Ok(plaintext), + Some(_) => Err(RpcError::Other( + "nip46 nip44_decrypt unexpected response".to_string(), + )), + None => Err(RpcError::Other( + "nip46 nip44_decrypt missing response".to_string(), + )), + } +} diff --git a/src/api/jsonrpc/methods/nip46/nip44_encrypt.rs b/src/api/jsonrpc/methods/nip46/nip44_encrypt.rs @@ -0,0 +1,73 @@ +use anyhow::Result; +use jsonrpsee::server::RpcModule; +use serde::{Deserialize, Serialize}; + +use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError}; +use crate::nip46::client; +use crate::nip46::session::Nip46Session; +use radroots_nostr::prelude::radroots_nostr_parse_pubkey; +use nostr::nips::nip46::{NostrConnectMethod, NostrConnectRequest, ResponseResult}; + +#[derive(Debug, Deserialize)] +struct Nip46Nip44EncryptParams { + session_id: String, + pubkey: String, + text: String, +} + +#[derive(Clone, Debug, Serialize)] +struct Nip46Nip44EncryptResponse { + ciphertext: String, +} + +pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { + registry.track("nip46.nip44_encrypt"); + m.register_async_method("nip46.nip44_encrypt", |params, ctx, _| async move { + let Nip46Nip44EncryptParams { + session_id, + pubkey, + text, + } = params + .parse() + .map_err(|e| RpcError::InvalidParams(e.to_string()))?; + let session = ctx + .state + .nip46_sessions + .get(&session_id) + .await + .ok_or_else(|| RpcError::InvalidParams("unknown session".to_string()))?; + let ciphertext = request_nip44_encrypt(&session, pubkey, text).await?; + Ok::<Nip46Nip44EncryptResponse, RpcError>(Nip46Nip44EncryptResponse { ciphertext }) + })?; + Ok(()) +} + +async fn request_nip44_encrypt( + session: &Nip46Session, + pubkey: String, + text: String, +) -> Result<String, RpcError> { + let public_key = radroots_nostr_parse_pubkey(&pubkey) + .map_err(|e| RpcError::InvalidParams(format!("invalid pubkey: {e}")))?; + let request = NostrConnectRequest::Nip44Encrypt { public_key, text }; + let response = client::request(session, request, "nip44_encrypt").await?; + let response = response + .to_response(NostrConnectMethod::Nip44Encrypt) + .map_err(|e| RpcError::Other(format!("nip46 nip44_encrypt failed: {e}")))?; + + if let Some(error) = response.error { + return Err(RpcError::Other(format!( + "nip46 nip44_encrypt error: {error}" + ))); + } + + match response.result { + Some(ResponseResult::Nip44Encrypt { ciphertext }) => Ok(ciphertext), + Some(_) => Err(RpcError::Other( + "nip46 nip44_encrypt unexpected response".to_string(), + )), + None => Err(RpcError::Other( + "nip46 nip44_encrypt missing response".to_string(), + )), + } +}