commit 6b66c168cb617b4184fa81ad1aeb8432be7c9659
parent 8331eb5bbfbd21e4de06f2673235fd1559ac7f07
Author: triesap <tyson@radroots.org>
Date: Fri, 27 Mar 2026 20:48:23 +0000
bridge: route listing publish through rr-rs signer backend
Diffstat:
5 files changed, 81 insertions(+), 19 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1772,6 +1772,7 @@ dependencies = [
name = "radroots-log"
version = "0.1.0-alpha.1"
dependencies = [
+ "chrono",
"thiserror 1.0.69",
"tracing",
"tracing-appender",
@@ -1794,6 +1795,34 @@ dependencies = [
]
[[package]]
+name = "radroots-nostr-connect"
+version = "0.1.0-alpha.1"
+dependencies = [
+ "nostr",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "url",
+]
+
+[[package]]
+name = "radroots-nostr-signer"
+version = "0.1.0-alpha.1"
+dependencies = [
+ "hex",
+ "nostr",
+ "radroots-identity",
+ "radroots-nostr-connect",
+ "radroots-runtime",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror 1.0.69",
+ "url",
+ "uuid",
+]
+
+[[package]]
name = "radroots-runtime"
version = "0.1.0-alpha.1"
dependencies = [
@@ -1835,6 +1864,7 @@ dependencies = [
"radroots-events-codec",
"radroots-identity",
"radroots-nostr",
+ "radroots-nostr-signer",
"radroots-runtime",
"radroots-trade",
"reqwest",
diff --git a/Cargo.toml b/Cargo.toml
@@ -16,6 +16,7 @@ radroots-events = { path = "../lib/crates/events" }
radroots-events-codec = { path = "../lib/crates/events-codec" }
radroots-identity = { path = "../lib/crates/identity" }
radroots-nostr = { path = "../lib/crates/nostr" }
+radroots-nostr-signer = { path = "../lib/crates/nostr-signer" }
radroots-runtime = { path = "../lib/crates/runtime" }
radroots-trade = { path = "../lib/crates/trade" }
@@ -28,6 +29,7 @@ radroots-events = { workspace = true, features = ["serde"] }
radroots-events-codec = { workspace = true, features = ["nostr", "serde_json"] }
radroots-identity = { workspace = true }
radroots-nostr = { workspace = true, features = ["client", "codec", "events", "http"] }
+radroots-nostr-signer = { workspace = true }
radroots-runtime = { workspace = true, features = ["cli"] }
radroots-trade = { workspace = true }
nostr = { version = "0.44.2", features = ["nip46"] }
diff --git a/src/app/runtime.rs b/src/app/runtime.rs
@@ -269,13 +269,13 @@ pub async fn run() -> Result<()> {
args.service.identity.as_ref(),
args.service.allow_generate_identity,
)?;
- let keys = identity.keys().clone();
let radrootsd = Radrootsd::new(
- keys,
+ identity.clone(),
settings.metadata.clone(),
settings.config.bridge.clone(),
settings.config.nip46.clone(),
);
+ let radrootsd = radrootsd?;
for relay in settings.config.service.relays.iter() {
radrootsd.client.add_relay(relay).await?;
@@ -332,7 +332,8 @@ mod tests {
use crate::core::Radrootsd;
use crate::transport::jsonrpc;
use radroots_events::kinds::KIND_LISTING;
- use radroots_nostr::prelude::{RadrootsNostrKeys, RadrootsNostrMetadata};
+ use radroots_identity::RadrootsIdentity;
+ use radroots_nostr::prelude::RadrootsNostrMetadata;
use std::path::PathBuf;
use std::sync::{Mutex, MutexGuard};
@@ -399,13 +400,14 @@ mod tests {
}
async fn make_handle(settings: &config::Settings) -> jsonrpsee::server::ServerHandle {
- let keys = RadrootsNostrKeys::generate();
+ let identity = RadrootsIdentity::generate();
let state = Radrootsd::new(
- keys,
+ identity,
settings.metadata.clone(),
settings.config.bridge.clone(),
settings.config.nip46.clone(),
- );
+ )
+ .expect("state");
jsonrpc::start_rpc(
state,
"127.0.0.1:0".parse().expect("addr"),
diff --git a/src/core/state.rs b/src/core/state.rs
@@ -1,6 +1,9 @@
+use anyhow::Result;
+use radroots_identity::RadrootsIdentity;
use radroots_nostr::prelude::{
RadrootsNostrClient, RadrootsNostrKeys, RadrootsNostrMetadata, RadrootsNostrPublicKey,
};
+use radroots_nostr_signer::prelude::RadrootsNostrEmbeddedSignerBackend;
use crate::app::config::{BridgeConfig, Nip46Config};
@@ -11,6 +14,7 @@ pub struct Radrootsd {
pub pubkey: RadrootsNostrPublicKey,
pub metadata: RadrootsNostrMetadata,
pub info: serde_json::Value,
+ pub bridge_signer: RadrootsNostrEmbeddedSignerBackend,
pub(crate) bridge_jobs: crate::core::bridge::store::BridgeJobStore,
pub bridge_config: BridgeConfig,
pub(crate) nip46_sessions: crate::core::nip46::session::Nip46SessionStore,
@@ -19,32 +23,35 @@ pub struct Radrootsd {
impl Radrootsd {
pub fn new(
- keys: RadrootsNostrKeys,
+ identity: RadrootsIdentity,
metadata: RadrootsNostrMetadata,
bridge_config: BridgeConfig,
nip46_config: Nip46Config,
- ) -> Self {
+ ) -> Result<Self> {
+ let keys: RadrootsNostrKeys = identity.keys().clone();
let pubkey = keys.public_key();
let client = RadrootsNostrClient::new(keys.clone());
let info = serde_json::json!({
"version": env!("CARGO_PKG_VERSION"),
"build": option_env!("GIT_HASH").unwrap_or("unknown"),
});
+ let bridge_signer = RadrootsNostrEmbeddedSignerBackend::new_in_memory(identity)?;
let bridge_jobs =
crate::core::bridge::store::BridgeJobStore::new(bridge_config.job_status_retention);
let nip46_sessions = crate::core::nip46::session::Nip46SessionStore::new();
- Self {
+ Ok(Self {
client,
keys,
pubkey,
metadata,
info,
+ bridge_signer,
bridge_jobs,
bridge_config,
nip46_sessions,
nip46_config,
- }
+ })
}
}
@@ -52,23 +59,26 @@ impl Radrootsd {
mod tests {
use super::Radrootsd;
use crate::app::config::{BridgeConfig, Nip46Config};
- use radroots_nostr::prelude::{RadrootsNostrKeys, RadrootsNostrMetadata};
+ use radroots_identity::RadrootsIdentity;
+ use radroots_nostr::prelude::RadrootsNostrMetadata;
+ use radroots_nostr_signer::prelude::RadrootsNostrSignerBackend;
#[test]
fn new_sets_core_fields() {
- let keys = RadrootsNostrKeys::generate();
+ let identity = RadrootsIdentity::generate();
let metadata: RadrootsNostrMetadata =
serde_json::from_str(r#"{"name":"radrootsd-test"}"#).expect("metadata");
let bridge_cfg = BridgeConfig::default();
let cfg = Nip46Config::default();
let state = Radrootsd::new(
- keys.clone(),
+ identity.clone(),
metadata.clone(),
bridge_cfg.clone(),
cfg.clone(),
- );
+ )
+ .expect("state");
- assert_eq!(state.pubkey, keys.public_key());
+ assert_eq!(state.pubkey, identity.public_key());
assert_eq!(state.metadata, metadata);
assert_eq!(state.bridge_config.enabled, bridge_cfg.enabled);
assert_eq!(
@@ -79,5 +89,11 @@ mod tests {
assert_eq!(state.nip46_config.perms, cfg.perms);
assert_eq!(state.info["version"], env!("CARGO_PKG_VERSION"));
assert_eq!(state.info["build"], "unknown");
+ let signer_identity = state
+ .bridge_signer
+ .signer_identity()
+ .expect("bridge signer identity")
+ .expect("present");
+ assert_eq!(signer_identity.public_key_hex, state.pubkey.to_hex());
}
}
diff --git a/src/transport/jsonrpc/methods/bridge/listing_publish.rs b/src/transport/jsonrpc/methods/bridge/listing_publish.rs
@@ -3,6 +3,7 @@ use jsonrpsee::server::RpcModule;
use radroots_events::listing::RadrootsListing;
use radroots_events_codec::listing::encode::to_wire_parts;
use radroots_nostr::prelude::{radroots_event_from_nostr, radroots_nostr_build_event};
+use radroots_nostr_signer::prelude::RadrootsNostrSignerBackend;
use radroots_trade::listing::validation::validate_listing_event;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@@ -45,15 +46,26 @@ async fn publish_listing(
}
let idempotency_key = normalize_idempotency_key(params.idempotency_key)?;
- let listing =
- canonicalize_listing_for_embedded_signer(params.listing, &ctx.state.pubkey.to_hex());
+ let signer_identity = ctx
+ .state
+ .bridge_signer
+ .signer_identity()
+ .map_err(|error| RpcError::Other(format!("bridge signer unavailable: {error}")))?
+ .ok_or_else(|| RpcError::Other("bridge signer identity is missing".to_string()))?;
+ let listing = canonicalize_listing_for_embedded_signer(
+ params.listing,
+ signer_identity.public_key_hex.as_str(),
+ );
let parts = to_wire_parts(&listing)
.map_err(|error| RpcError::InvalidParams(format!("invalid listing contract: {error}")))?;
let builder = radroots_nostr_build_event(parts.kind, parts.content, parts.tags)
.map_err(|error| RpcError::Other(format!("failed to build listing event: {error}")))?;
- let event = builder
- .sign_with_keys(&ctx.state.keys)
+ let signed = ctx
+ .state
+ .bridge_signer
+ .sign_event_builder(builder)
.map_err(|error| RpcError::Other(format!("failed to sign listing event: {error}")))?;
+ let event = signed.event;
let canonical = radroots_event_from_nostr(&event);
let validated = validate_listing_event(&canonical)
.map_err(|error| RpcError::InvalidParams(format!("invalid listing contract: {error}")))?;