cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit ff7ff71237cdfadaec68cfd0d290e17bfcad7b7e
parent 8723f150629ee407dcd29dcb8174e972814b9b0f
Author: triesap <tyson@radroots.org>
Date:   Sat,  9 May 2026 00:30:40 +0000

cli: route order submit through sdk

- enable radroots_sdk relay-client and signing features for order submit
- publish active order requests through the shared sdk relay path
- map sdk relay receipts back into existing order submit output
- cover relay-direct local-identity sdk client wiring in unit tests

Diffstat:
MCargo.lock | 1+
MCargo.toml | 2+-
Msrc/runtime/order.rs | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
3 files changed, 98 insertions(+), 31 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2090,6 +2090,7 @@ dependencies = [ "radroots_events", "radroots_events_codec", "radroots_identity", + "radroots_nostr", "radroots_trade", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml @@ -37,7 +37,7 @@ radroots_replica_db = { path = "../lib/crates/replica_db" } radroots_replica_db_schema = { path = "../lib/crates/replica_db_schema" } radroots_replica_sync = { path = "../lib/crates/replica_sync" } radroots_runtime_paths = { path = "../lib/crates/runtime_paths" } -radroots_sdk = { path = "../lib/crates/sdk", features = ["radrootsd-client"] } +radroots_sdk = { path = "../lib/crates/sdk", features = ["radrootsd-client", "relay-client", "signing"] } radroots_secret_vault = { path = "../lib/crates/secret_vault", features = ["std", "os-keyring"] } radroots_sql_core = { path = "../lib/crates/sql_core", features = ["native"] } radroots_trade = { path = "../lib/crates/trade" } diff --git a/src/runtime/order.rs b/src/runtime/order.rs @@ -38,8 +38,7 @@ use radroots_events_codec::trade::{ active_trade_event_context_from_tags, active_trade_fulfillment_update_event_build, active_trade_fulfillment_update_from_event, active_trade_order_cancel_event_build, active_trade_order_cancel_from_event, active_trade_order_decision_event_build, - active_trade_order_request_event_build, active_trade_order_request_from_event, - active_trade_order_revision_decision_event_build, + active_trade_order_request_from_event, active_trade_order_revision_decision_event_build, active_trade_order_revision_decision_from_event, active_trade_order_revision_proposal_event_build, active_trade_order_revision_proposal_from_event, active_trade_payment_recorded_event_build, @@ -60,6 +59,10 @@ use radroots_replica_db_schema::nostr_event_state::{ use radroots_replica_db_schema::trade_product::{ ITradeProductFieldsFilter, ITradeProductFindMany, TradeProduct, }; +use radroots_sdk::{ + RadrootsSdkClient, RadrootsSdkConfig, SdkEnvironment, SdkPublishError, SdkPublishReceipt, + SdkRelayFailure, SdkTransportMode, SdkTransportReceipt, SignerConfig as SdkSignerConfig, +}; use radroots_sql_core::SqliteExecutor; use radroots_trade::order::{ RadrootsActiveOrderCancellationRecord, RadrootsActiveOrderDecisionRecord, @@ -9713,36 +9716,66 @@ fn publish_order_request( id: loaded.document.order.listing_event_id.clone(), relays: None, }; - let parts = active_trade_order_request_event_build(&listing_event, &payload) - .map_err(|error| RuntimeError::Config(format!("encode order request event: {error}")))?; - let event_kind = parts.kind; - let receipt = publish_parts_with_identity(&signing.identity, &config.relay.urls, parts) - .map_err(|error| RuntimeError::Network(error.to_string()))?; + let client = order_relay_publish_client(config)?; + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .map_err(|error| { + RuntimeError::Network(format!("build relay order submit runtime: {error}")) + })?; + let receipt = runtime + .block_on(client.trade().publish_order_request_with_identity( + &signing.identity, + &listing_event, + &payload, + )) + .map_err(map_sdk_order_submit_error)?; - Ok(published_order_submit_view( - config, loaded, args, event_kind, receipt, - )) + published_order_submit_view(config, loaded, args, receipt) +} + +fn order_relay_publish_client(config: &RuntimeConfig) -> Result<RadrootsSdkClient, RuntimeError> { + let mut sdk_config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom); + sdk_config.transport = SdkTransportMode::RelayDirect; + sdk_config.signer = SdkSignerConfig::LocalIdentity; + sdk_config.relay.urls = config.relay.urls.clone(); + RadrootsSdkClient::from_config(sdk_config) + .map_err(|error| RuntimeError::Config(format!("configure relay order submit: {error}"))) +} + +fn map_sdk_order_submit_error(error: SdkPublishError) -> RuntimeError { + let message = format!("relay order submit failed: {error}"); + match error { + SdkPublishError::Config(_) + | SdkPublishError::Encode(_) + | SdkPublishError::UnsupportedTransport { .. } + | SdkPublishError::UnsupportedSignerMode { .. } => RuntimeError::Config(message), + SdkPublishError::Relay(_) + | SdkPublishError::RelaySetup { .. } + | SdkPublishError::RelayNotAcknowledged { .. } + | SdkPublishError::Radrootsd(_) => RuntimeError::Network(message), + } } fn published_order_submit_view( config: &RuntimeConfig, loaded: &LoadedOrderDraft, args: &OrderSubmitArgs, - event_kind: u32, - receipt: DirectRelayPublishReceipt, -) -> OrderSubmitView { - let DirectRelayPublishReceipt { - event: _, + receipt: SdkPublishReceipt, +) -> Result<OrderSubmitView, RuntimeError> { + let SdkPublishReceipt { event_id, - created_at: _, - signature: _, - target_relays, - connected_relays, - acknowledged_relays, - failed_relays, + event_kind, + transport_receipt, + .. } = receipt; + let SdkTransportReceipt::RelayDirect(relay) = transport_receipt else { + return Err(RuntimeError::Config( + "relay order submit returned a non-relay transport receipt".to_owned(), + )); + }; - OrderSubmitView { + Ok(OrderSubmitView { state: "submitted".to_owned(), source: ORDER_SUBMIT_SOURCE.to_owned(), order_id: loaded.document.order.order_id.clone(), @@ -9753,14 +9786,14 @@ fn published_order_submit_view( buyer_account_id: loaded.document.buyer_account_id.clone(), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), - event_id: Some(event_id), - event_kind: Some(event_kind), + event_id: event_id.or(Some(relay.event_id)), + event_kind: event_kind.or(Some(relay.event_kind)), dry_run: false, deduplicated: false, - target_relays, - connected_relays, - acknowledged_relays, - failed_relays: relay_failures(failed_relays), + target_relays: relay.target_relays, + connected_relays: relay.connected_relays, + acknowledged_relays: relay.acknowledged_relays, + failed_relays: sdk_relay_failures(relay.failed_relays), idempotency_key: args.idempotency_key.clone(), signer_mode: Some(config.signer.backend.as_str().to_owned()), signer_session_id: None, @@ -9769,7 +9802,7 @@ fn published_order_submit_view( job: None, issues: Vec::new(), actions: Vec::new(), - } + }) } fn order_binding_error_view( @@ -10071,6 +10104,16 @@ fn relay_failures(failures: Vec<DirectRelayFailure>) -> Vec<RelayFailureView> { .collect() } +fn sdk_relay_failures(failures: Vec<SdkRelayFailure>) -> Vec<RelayFailureView> { + failures + .into_iter() + .map(|failure| RelayFailureView { + relay: failure.relay_url, + reason: failure.error, + }) + .collect() +} + fn load_draft(path: &Path) -> Result<LoadedOrderDraft, String> { let contents = fs::read_to_string(path) .map_err(|error| format!("read order draft {}: {error}", path.display()))?; @@ -10371,7 +10414,7 @@ mod tests { order_history_from_receipt, order_payment_dry_run_view, order_payment_event_parts, order_payment_payload_from_status, order_payment_preflight_view_from_status, order_receipt_dry_run_view, order_receipt_event_parts, order_receipt_payload_from_status, - order_receipt_preflight_view_from_status, order_request_filter, + order_receipt_preflight_view_from_status, order_relay_publish_client, order_request_filter, order_revision_decision_event_parts, order_revision_decision_payload_from_proposal, order_revision_decision_preflight_view_from_status, order_revision_event_parts, order_revision_inventory_preflight_view, order_revision_payload_from_status, @@ -10439,6 +10482,29 @@ mod tests { } #[test] + fn order_relay_publish_client_uses_configured_relay_and_local_identity() { + let dir = tempdir().expect("tempdir"); + let mut config = sample_config(dir.path()); + config.relay.urls = vec!["ws://127.0.0.1:9001".to_owned()]; + + let client = order_relay_publish_client(&config).expect("relay order publish client"); + + assert_eq!( + client.transport(), + radroots_sdk::SdkTransportMode::RelayDirect + ); + assert_eq!(client.signer(), radroots_sdk::SignerConfig::LocalIdentity); + match client.resolved_transport_target() { + radroots_sdk::SdkResolvedTransportTarget::RelayDirect { relay_urls } => { + assert_eq!(relay_urls.as_slice(), ["ws://127.0.0.1:9001"]); + } + radroots_sdk::SdkResolvedTransportTarget::Radrootsd { .. } => { + panic!("order submit must use relay direct transport"); + } + } + } + + #[test] fn order_economics_applies_listing_discounts_and_basket_adjustments() { let listing = ResolvedOrderListing { listing_addr: "30402:seller:AAAAAAAAAAAAAAAAAAAAAg".to_owned(),