commit add9743dae423065a7e84328993eafa78669090c
parent a778f2d58f4f05c09d281e43d25eaf64eb774421
Author: triesap <tyson@radroots.org>
Date: Mon, 6 Jul 2026 22:27:56 +0000
sdk: add transport profile API
- replace relay-target builder state with transport profile configuration
- expose SDK transport target and profile models
- construct typed outbox delivery plans for workflow enqueue
- update runtime tests and README for target-state transport APIs
Diffstat:
38 files changed, 2038 insertions(+), 1414 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1776,6 +1776,7 @@ version = "0.1.0-alpha.2"
dependencies = [
"radroots_events",
"radroots_nostr",
+ "radroots_transport",
"serde",
"serde_json",
"sqlx",
@@ -1944,6 +1945,7 @@ dependencies = [
"hex",
"radroots_event_store",
"radroots_events",
+ "radroots_transport",
"serde",
"serde_json",
"sha2",
@@ -1971,23 +1973,6 @@ dependencies = [
]
[[package]]
-name = "radroots_relay_transport"
-version = "0.1.0-alpha.2"
-dependencies = [
- "futures",
- "nostr",
- "radroots_event_store",
- "radroots_events",
- "radroots_nostr",
- "radroots_outbox",
- "serde",
- "serde_json",
- "thiserror 1.0.69",
- "tokio",
- "url",
-]
-
-[[package]]
name = "radroots_replica_db"
version = "0.1.0-alpha.2"
dependencies = [
@@ -2118,13 +2103,15 @@ dependencies = [
"radroots_nostr_signer",
"radroots_outbox",
"radroots_publish_proxy_protocol",
- "radroots_relay_transport",
"radroots_replica_db",
"radroots_replica_db_schema",
"radroots_replica_sync",
"radroots_runtime_paths",
"radroots_sql_core",
"radroots_trade",
+ "radroots_transport",
+ "radroots_transport_nostr",
+ "radroots_transport_reticulum",
"reqwest",
"serde",
"serde_json",
@@ -2214,6 +2201,39 @@ dependencies = [
]
[[package]]
+name = "radroots_transport"
+version = "0.1.0-alpha.2"
+dependencies = [
+ "serde",
+ "sha2",
+]
+
+[[package]]
+name = "radroots_transport_nostr"
+version = "0.1.0-alpha.2"
+dependencies = [
+ "futures",
+ "nostr",
+ "radroots_event_store",
+ "radroots_events",
+ "radroots_nostr",
+ "radroots_outbox",
+ "radroots_transport",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "radroots_transport_reticulum"
+version = "0.1.0-alpha.2"
+dependencies = [
+ "radroots_transport",
+]
+
+[[package]]
name = "radroots_types"
version = "0.1.0-alpha.2"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
@@ -44,7 +44,9 @@ radroots_nostr_connect = { path = "../lib/crates/nostr_connect", version = "0.1.
radroots_nostr_signer = { path = "../lib/crates/nostr_signer", version = "0.1.0-alpha.2", default-features = false }
radroots_outbox = { path = "../lib/crates/outbox", version = "0.1.0-alpha.2", default-features = false }
radroots_publish_proxy_protocol = { path = "../lib/crates/publish_proxy_protocol", version = "0.1.0-alpha.2", default-features = false }
-radroots_relay_transport = { path = "../lib/crates/relay_transport", version = "0.1.0-alpha.2", default-features = false }
+radroots_transport = { path = "../lib/crates/transport", version = "0.1.0-alpha.2", default-features = false }
+radroots_transport_nostr = { path = "../lib/crates/transport_nostr", version = "0.1.0-alpha.2", default-features = false }
+radroots_transport_reticulum = { path = "../lib/crates/transport_reticulum", version = "0.1.0-alpha.2", default-features = false }
radroots_replica_db = { path = "../lib/crates/replica_db", version = "0.1.0-alpha.2", default-features = false }
radroots_replica_db_schema = { path = "../lib/crates/replica_db_schema", version = "0.1.0-alpha.2", default-features = false }
radroots_replica_sync = { path = "../lib/crates/replica_sync", version = "0.1.0-alpha.2", default-features = false }
diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml
@@ -43,11 +43,11 @@ radrootsd-proxy = [
"serde_json",
"dep:futures",
"dep:radroots_publish_proxy_protocol",
- "dep:radroots_relay_transport",
+ "dep:radroots_transport_nostr",
"dep:reqwest",
"radroots_publish_proxy_protocol/serde",
"radroots_publish_proxy_protocol/std",
- "radroots_relay_transport/std",
+ "radroots_transport_nostr/std",
]
signer-adapters = [
"identity-models",
@@ -68,7 +68,9 @@ runtime = [
"dep:radroots_event_store",
"dep:radroots_geocoder",
"dep:radroots_outbox",
- "dep:radroots_relay_transport",
+ "dep:radroots_transport",
+ "dep:radroots_transport_nostr",
+ "dep:radroots_transport_reticulum",
"dep:radroots_runtime_paths",
"dep:sha2",
"dep:sqlx",
@@ -78,9 +80,10 @@ runtime = [
"radroots_event_store/runtime-tokio",
"radroots_outbox/sqlite",
"radroots_outbox/runtime-tokio",
- "radroots_relay_transport/std",
- "radroots_relay_transport/storage",
- "radroots_relay_transport/runtime-tokio",
+ "radroots_transport/serde",
+ "radroots_transport_nostr/std",
+ "radroots_transport_nostr/storage",
+ "radroots_transport_nostr/runtime-tokio",
"radroots_trade/event_store",
]
local-signer = ["runtime", "radroots_authority/local_signer"]
@@ -88,7 +91,7 @@ relay-runtime = [
"runtime",
"dep:radroots_nostr",
"radroots_nostr/client",
- "radroots_relay_transport/client",
+ "radroots_transport_nostr/client",
]
local-runtime = [
"std",
@@ -121,7 +124,9 @@ radroots_events_codec = { workspace = true, default-features = false }
radroots_geocoder = { workspace = true, optional = true }
radroots_outbox = { workspace = true, optional = true, default-features = false }
radroots_publish_proxy_protocol = { workspace = true, optional = true, default-features = false }
-radroots_relay_transport = { workspace = true, optional = true, default-features = false }
+radroots_transport = { workspace = true, optional = true, default-features = false }
+radroots_transport_nostr = { workspace = true, optional = true, default-features = false }
+radroots_transport_reticulum = { workspace = true, optional = true, default-features = false }
radroots_runtime_paths = { workspace = true, optional = true, default-features = false }
radroots_trade = { workspace = true, default-features = false }
radroots_identity = { workspace = true, optional = true, default-features = false }
diff --git a/crates/sdk/README b/crates/sdk/README
@@ -5,10 +5,11 @@ Curated Radroots Rust SDK for local-first Radroots product workflows.
The SDK v1 product runtime is centered on `RadrootsClient::builder()`,
`sdk.farms()`, `sdk.listings()`, `sdk.trades()`, `sdk.market()`, and `sdk.sync()`.
-`RadrootsClient::builder()` defaults to memory storage, the system clock, no relay URLs, and no
-production network publishing. Directory storage is opt-in and creates `event_store.sqlite` and
-`outbox.sqlite` in the selected directory. Configured relay URLs are opt-in enqueue defaults used
-when a publish request chooses `SdkRelayTargetPolicy::UseConfiguredRelays`.
+`RadrootsClient::builder()` defaults to memory storage, the system clock, the `LocalOnly` transport
+profile, and no production network publishing. Directory storage is opt-in and creates
+`event_store.sqlite` and `outbox.sqlite` in the selected directory. Configured Nostr relay URLs live
+inside `TransportProfile::Nostr` or the Nostr side of `TransportProfile::Hybrid`, and product
+enqueue requests choose them through `TargetPolicy::UseConfiguredProfile`.
When `signer-adapters` is enabled, `RadrootsClient::builder()` accepts a configured
`RadrootsSdkSignerProvider`. The production signing modes are `local_key` and `myc_nip46`. Product
@@ -21,8 +22,8 @@ farm, listing, or trade write call. `signer_status()`, `configured_signer()`, an
that prepared plan, ingests it into the local event store, queues signed outbox work, and returns a
typed `ListingEnqueueReceipt`. `sdk.listings().enqueue_publish(...)` is the convenience path that
prepares once and delegates to `enqueue_prepared_publish(...)`. Farm and trade write methods follow
-the same configured signer pattern. The enqueue path uses typed relay target and idempotency inputs;
-omitted idempotency keys are derived deterministically.
+the same configured signer pattern. The enqueue path uses typed transport target and idempotency
+inputs; omitted idempotency keys are derived deterministically.
Explicit signer injection remains available under `*_with_explicit_signer` method names for
controlled adapter-level tests and advanced integration checks. Those methods are not the primary
@@ -34,8 +35,8 @@ clock at enqueue time. Listing enqueue receipts report mutation state with the p
`StoredAndQueued` and `AlreadyQueued`.
`sdk.sync().push_outbox(...)` is the product sync entrypoint. It publishes queued signed outbox work
-when `relay-runtime` is enabled. Push time uses the relay targets already stored on each queued
-outbox event, so already queued work does not require configured builder relays. Direct relay
+when `relay-runtime` is enabled. Push time uses the delivery targets already stored on each queued
+outbox event, so already queued work does not require a configured builder profile. Nostr transport
publishing and the `radrootsd-proxy` Publish Proxy transport consume signed outbox events; neither
transport owns signing. `push_outbox_with_adapter(...)` remains available for tests and controlled
adapter-level substrate checks. `radrootsd-proxy` adds daemon-resolved publishing through
@@ -59,8 +60,8 @@ DTOs. DVM proof requests and validation receipt ingestion use `sdk.dvm()`.
The `local-runtime` feature is the curated feature bundle for local product runtime consumers. It
enables `std`, `serde`, `serde_json`, `runtime`, `signer-adapters`, `relay-runtime`, and
`relay-client`. `signer-adapters` contains the SDK `local_key` and `myc_nip46` signing surface.
-`relay-client` supplies the relay WebSocket publish adapter used when signed outbox work targets
-configured relay URLs. `local-runtime-radrootsd-proxy` uses daemon-resolved publishing through
+`relay-client` supplies the Nostr relay WebSocket publish adapter used when signed outbox work
+targets Nostr relay URLs. `local-runtime-radrootsd-proxy` uses daemon-resolved publishing through
`publish.event` and the same configured signer provider API.
`radroots_sdk_myc_nip46_product_permissions()` and
@@ -69,7 +70,7 @@ configured relay URLs. `local-runtime-radrootsd-proxy` uses daemon-resolved publ
listing, and trade event kinds the SDK can write.
Relay URL policy is explicit. Public relay URLs must use `wss://`. Local development `ws://` relay
-URLs are accepted only under `SdkRelayUrlPolicy::Localhost` and only for `localhost`, `127.0.0.1`,
+URLs are accepted only under `NostrRelayUrlPolicy::Localhost` and only for `localhost`, `127.0.0.1`,
or `[::1]`. Non-local insecure `ws://` targets, including private LAN addresses, are rejected.
Low-level event-contract, wire-codec, reducer, and DVM contract ownership lives in the shared
@@ -92,7 +93,7 @@ cargo check -p radroots_sdk --example sdk_v1_myc_nip46_signer_setup --features r
`sdk_v1_knowledge_prepare` shows `radroots_sdk::knowledge`, typed event builders, frozen draft
preparation, signed-event verification, decoded event matching, and manifest hash access without
local runtime storage.
-`sdk_v1_local_enqueue_and_mock_sync` shows localhost relay target selection, configured local-key
+`sdk_v1_local_enqueue_and_mock_sync` shows localhost Nostr target selection, configured local-key
signing, prepared listing enqueue, `push_outbox_with_adapter(...)` with a mock relay adapter, and
`TradeStatusRequest`. `sdk_v1_myc_nip46_signer_setup` shows Myc NIP-46 signer provider setup and
product permission derivation at the SDK boundary. The examples stay on product APIs and do not use
@@ -126,8 +127,8 @@ Runtime request DTOs are constructor-led and marked non-exhaustive where they ca
`parse`, `default`, and `with_*` methods rather than struct literals.
Runtime enums that may gain variants are non-exhaustive. This includes storage, clock,
-relay-target, mutation-state, trade-status, sync-status, relay-auth, and push-outbox state or
-outcome enums.
+target-policy, transport-profile, mutation-state, trade-status, sync-status, relay-auth, and
+push-outbox state or outcome enums.
Runtime receipts and status records expose stable serialized public fields for CLI and local-runtime
reporting. Future additive reporting must add fields without changing existing serialized field
diff --git a/crates/sdk/examples/runtime_local.rs b/crates/sdk/examples/runtime_local.rs
@@ -9,9 +9,9 @@ use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId};
use radroots_events::listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct};
use radroots_nostr::prelude::RadrootsNostrKeys;
use radroots_sdk::{
- ListingPreparePublishRequest, PushOutboxRequest, RadrootsClient, RadrootsSdkError,
- RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SdkIdempotencyKey,
- SdkRelayTargetPolicy, SdkRelayUrlPolicy, TradeStatusRequest,
+ ListingPreparePublishRequest, NostrRelayUrlPolicy, PushOutboxRequest, RadrootsClient,
+ RadrootsSdkError, RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp,
+ SdkIdempotencyKey, TargetPolicy, TradeStatusRequest,
};
const RELAY: &str = "wss://relay.example.com";
@@ -29,7 +29,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let actor = RadrootsActorContext::test(seller.as_str(), [RadrootsActorRole::Seller])?;
let listing = sample_listing(seller.as_str());
let prepare_request = ListingPreparePublishRequest::new(actor.clone(), listing);
- let target_relays = SdkRelayTargetPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)?;
+ let target_relays = TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public)?;
let idempotency_key = SdkIdempotencyKey::new("example-1")?;
let prepared = sdk.listings().prepare_publish(prepare_request)?;
diff --git a/crates/sdk/examples/sdk_v1_local_enqueue_and_mock_sync.rs b/crates/sdk/examples/sdk_v1_local_enqueue_and_mock_sync.rs
@@ -8,12 +8,12 @@ use radroots_events::farm::RadrootsFarmRef;
use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId};
use radroots_events::listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct};
use radroots_nostr::prelude::RadrootsNostrKeys;
-use radroots_relay_transport::RadrootsMockRelayPublishAdapter;
use radroots_sdk::{
- ListingPreparePublishRequest, PushOutboxRequest, RadrootsClient, RadrootsSdkLocalKeySigner,
- RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkRelayTargetPolicy,
- SdkRelayTargetSet, SdkRelayUrlPolicy, TradeStatusRequest,
+ ListingPreparePublishRequest, NostrRelayUrlPolicy, PushOutboxRequest, RadrootsClient,
+ RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SdkIdempotencyKey,
+ TargetPolicy, TargetSet, TradeStatusRequest,
};
+use radroots_transport_nostr::RadrootsMockRelayPublishAdapter;
const LOCAL_RELAY: &str = "ws://localhost:7777";
@@ -28,8 +28,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.build()
.await?;
let actor = RadrootsActorContext::test(seller.as_str(), [RadrootsActorRole::Seller])?;
- let targets = SdkRelayTargetSet::new([LOCAL_RELAY], SdkRelayUrlPolicy::Localhost)?;
- let target_policy = SdkRelayTargetPolicy::explicit(targets);
+ let targets = TargetSet::new([LOCAL_RELAY], NostrRelayUrlPolicy::Localhost)?;
+ let target_policy = TargetPolicy::explicit(targets);
let prepared = sdk
.listings()
diff --git a/crates/sdk/src/adapters/radrootsd.rs b/crates/sdk/src/adapters/radrootsd.rs
@@ -6,7 +6,8 @@ use radroots_publish_proxy_protocol::{
METHOD_EVENT, PublishDeliveryPolicy, PublishEventRequest, PublishEventResponse,
PublishProxyProtocolError, PublishRelayOutcomeKind, PublishRelayPolicy, SignedNostrEventWire,
};
-use radroots_relay_transport::{
+use radroots_transport::RadrootsTransportSatisfactionPolicy;
+use radroots_transport_nostr::{
RadrootsRelayOutcome, RadrootsRelayOutcomeKind, RadrootsRelayPublishAdapter,
RadrootsRelayPublishReceipt, RadrootsRelayPublishRelayReceipt, RadrootsRelayPublishRequest,
RadrootsRelayTransportError,
@@ -120,8 +121,8 @@ impl RadrootsRelayPublishAdapter for RadrootsdProxyPublishAdapter {
let request = RadrootsdProxyPublishRequest {
delivery_policy: delivery_policy_from_relay_request(
request.targets.len(),
- request.accepted_quorum,
- ),
+ &request.satisfaction_policy,
+ )?,
signed_event: request.signed_event,
relays: request.targets.relay_strings(),
idempotency_key: None,
@@ -353,17 +354,17 @@ fn signed_event_wire(event: &RadrootsSignedNostrEvent) -> SignedNostrEventWire {
fn delivery_policy_from_relay_request(
target_count: usize,
- accepted_quorum: usize,
-) -> PublishDeliveryPolicy {
- if accepted_quorum >= target_count {
+ satisfaction_policy: &RadrootsTransportSatisfactionPolicy,
+) -> Result<PublishDeliveryPolicy, RadrootsRelayTransportError> {
+ let required = satisfaction_policy.required_target_count(target_count)?;
+ let delivery_policy = if required >= target_count {
PublishDeliveryPolicy::All
- } else if accepted_quorum <= 1 {
+ } else if required <= 1 {
PublishDeliveryPolicy::Any
} else {
- PublishDeliveryPolicy::Quorum {
- quorum: accepted_quorum,
- }
- }
+ PublishDeliveryPolicy::Quorum { quorum: required }
+ };
+ Ok(delivery_policy)
}
fn proxy_receipt_from_response(
diff --git a/crates/sdk/src/dvm_runtime.rs b/crates/sdk/src/dvm_runtime.rs
@@ -2,9 +2,9 @@
use crate::workflow_runtime::enqueue_configured_signed_workflow;
#[cfg(feature = "runtime")]
use crate::{
- DvmClient, RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkMutationState,
- SdkRelayTargetPolicy, SdkRelayUrlPolicy, SyncProjectionRefreshReceipt,
- SyncProjectionRefreshRequest,
+ DvmClient, NostrRelayUrlPolicy, RadrootsSdkError, RadrootsSdkTimestamp, SatisfactionPolicy,
+ SdkIdempotencyKey, SdkMutationState, SyncProjectionRefreshReceipt,
+ SyncProjectionRefreshRequest, TargetPolicy,
runtime::sdk_now_ms,
sync_runtime::refresh_product_projections_for_sdk,
workflow_runtime::{SdkWorkflowEnqueueRequest, enqueue_signed_workflow},
@@ -174,7 +174,7 @@ impl DvmTradeTransitionProofPrepareRequest {
pub struct DvmTradeTransitionProofEnqueueRequest {
#[serde(flatten)]
pub prepare: DvmTradeTransitionProofPrepareRequest,
- pub target_relays: SdkRelayTargetPolicy,
+ pub target_relays: TargetPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
}
@@ -188,7 +188,7 @@ impl DvmTradeTransitionProofEnqueueRequest {
request_event_id: RadrootsEventId,
decision_event_id: RadrootsEventId,
inventory_bins: Vec<RadrootsTradeInventoryBinWitnessDto>,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
) -> Self {
Self::from_prepare(
DvmTradeTransitionProofPrepareRequest::new(
@@ -206,7 +206,7 @@ impl DvmTradeTransitionProofEnqueueRequest {
pub fn from_prepare(
prepare: DvmTradeTransitionProofPrepareRequest,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
) -> Self {
Self {
prepare,
@@ -218,13 +218,13 @@ impl DvmTradeTransitionProofEnqueueRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = SdkRelayTargetPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -430,6 +430,7 @@ impl<'sdk> DvmClient<'sdk> {
actor: &actor,
frozen_draft: &plan.frozen_draft,
target_relays,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key,
},
)
@@ -453,6 +454,7 @@ impl<'sdk> DvmClient<'sdk> {
actor: &actor,
frozen_draft: &plan.frozen_draft,
target_relays,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key,
},
signer,
diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs
@@ -823,7 +823,7 @@ impl From<radroots_trade::projection::RadrootsTradeProjectionError> for Radroots
impl From<radroots_outbox::RadrootsOutboxError> for RadrootsSdkError {
fn from(error: radroots_outbox::RadrootsOutboxError) -> Self {
match error {
- radroots_outbox::RadrootsOutboxError::EmptyTargetRelays => {
+ radroots_outbox::RadrootsOutboxError::EmptyDeliveryTargets => {
Self::empty_target_relays("outbox enqueue")
}
radroots_outbox::RadrootsOutboxError::IdempotencyConflict {
@@ -846,34 +846,48 @@ impl From<radroots_outbox::RadrootsOutboxError> for RadrootsSdkError {
}
#[cfg(feature = "runtime")]
-impl From<radroots_relay_transport::RadrootsRelayTransportError> for RadrootsSdkError {
- fn from(error: radroots_relay_transport::RadrootsRelayTransportError) -> Self {
+impl From<radroots_transport::RadrootsTransportError> for RadrootsSdkError {
+ fn from(error: radroots_transport::RadrootsTransportError) -> Self {
match error {
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlParse {
+ radroots_transport::RadrootsTransportError::EmptyTargetSet => {
+ Self::empty_target_relays("transport target set")
+ }
+ error => Self::RelayTransport {
+ message: error.to_string(),
+ },
+ }
+ }
+}
+
+#[cfg(feature = "runtime")]
+impl From<radroots_transport_nostr::RadrootsRelayTransportError> for RadrootsSdkError {
+ fn from(error: radroots_transport_nostr::RadrootsRelayTransportError) -> Self {
+ match error {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlParse {
url,
reason,
} => Self::invalid_relay_url(url, reason),
- radroots_relay_transport::RadrootsRelayTransportError::WsRequiresLocalhostPolicy {
+ radroots_transport_nostr::RadrootsRelayTransportError::WsRequiresLocalhostPolicy {
url,
} => Self::invalid_relay_url(url, "ws relay URL requires localhost policy"),
- radroots_relay_transport::RadrootsRelayTransportError::UnsupportedRelayScheme {
+ radroots_transport_nostr::RadrootsRelayTransportError::UnsupportedRelayScheme {
url,
scheme,
} => Self::invalid_relay_url(url, format!("unsupported scheme `{scheme}`")),
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlUserinfo { url } => {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlUserinfo { url } => {
Self::invalid_relay_url(url, "relay URL must not include userinfo")
}
- radroots_relay_transport::RadrootsRelayTransportError::EmptyRelayHost { url } => {
+ radroots_transport_nostr::RadrootsRelayTransportError::EmptyRelayHost { url } => {
Self::invalid_relay_url(url, "relay URL must include a host")
}
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlQueryOrFragment {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlQueryOrFragment {
url,
} => Self::invalid_relay_url(url, "relay URL must not include query or fragment"),
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlForbiddenDestination {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlForbiddenDestination {
url,
reason,
} => Self::invalid_relay_url(url, reason),
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlResolvedForbiddenDestination {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlResolvedForbiddenDestination {
url,
address,
reason,
@@ -881,11 +895,11 @@ impl From<radroots_relay_transport::RadrootsRelayTransportError> for RadrootsSdk
url,
format!("relay URL resolved to forbidden address `{address}`: {reason}"),
),
- radroots_relay_transport::RadrootsRelayTransportError::EmptyTargetSet => {
+ radroots_transport_nostr::RadrootsRelayTransportError::EmptyTargetSet => {
Self::empty_target_relays("relay publish")
}
#[cfg(feature = "runtime")]
- radroots_relay_transport::RadrootsRelayTransportError::Outbox(error) => error.into(),
+ radroots_transport_nostr::RadrootsRelayTransportError::Outbox(error) => error.into(),
error => Self::RelayTransport {
message: error.to_string(),
},
diff --git a/crates/sdk/src/farms_runtime.rs b/crates/sdk/src/farms_runtime.rs
@@ -2,8 +2,8 @@
use crate::workflow_runtime::enqueue_configured_signed_workflow;
#[cfg(feature = "runtime")]
use crate::{
- FarmsClient, RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkMutationState,
- SdkRelayTargetPolicy, SdkRelayUrlPolicy, farm,
+ FarmsClient, NostrRelayUrlPolicy, RadrootsSdkError, RadrootsSdkTimestamp, SatisfactionPolicy,
+ SdkIdempotencyKey, SdkMutationState, TargetPolicy, farm,
geonames::{
Geocoder, GeocoderLocalityCandidate, GeocoderLocalityLookup, GeocoderLocalityQuery,
GeocoderPoint, GeocoderReverseOptions, GeocoderReverseResult,
@@ -69,7 +69,7 @@ pub struct FarmEnqueuePublishRequest {
#[serde(serialize_with = "crate::actor_json::serialize_actor_context")]
pub actor: RadrootsActorContext,
pub farm: RadrootsFarm,
- pub target_relays: SdkRelayTargetPolicy,
+ pub target_relays: TargetPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -79,7 +79,7 @@ impl FarmEnqueuePublishRequest {
pub fn new(
actor: RadrootsActorContext,
farm: RadrootsFarm,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
) -> Self {
Self {
actor,
@@ -93,13 +93,13 @@ impl FarmEnqueuePublishRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = SdkRelayTargetPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -471,7 +471,7 @@ impl<'sdk> FarmsClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: FarmPublishPlan,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<FarmEnqueueReceipt, RadrootsSdkError> {
let enqueue = enqueue_configured_signed_workflow(
@@ -481,6 +481,7 @@ impl<'sdk> FarmsClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key,
},
)
@@ -492,7 +493,7 @@ impl<'sdk> FarmsClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: FarmPublishPlan,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<FarmEnqueueReceipt, RadrootsSdkError> {
@@ -503,6 +504,7 @@ impl<'sdk> FarmsClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key,
},
signer,
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
@@ -44,8 +44,6 @@ mod private_store;
#[cfg(feature = "runtime")]
mod product_clients;
#[cfg(feature = "runtime")]
-mod relay_targets;
-#[cfg(feature = "runtime")]
mod runtime;
#[cfg(all(feature = "runtime", feature = "signer-adapters"))]
mod signer_provider;
@@ -54,6 +52,8 @@ mod sync_runtime;
#[cfg(feature = "runtime")]
mod trade_storage;
#[cfg(feature = "runtime")]
+pub mod transport;
+#[cfg(feature = "runtime")]
mod workflow_runtime;
#[cfg(feature = "runtime")]
@@ -193,19 +193,14 @@ pub use crate::product_clients::{
TradeResyncClient, TradeSellerClient, TradeValidationReceiptsClient, TradesClient,
};
#[cfg(feature = "runtime")]
-pub use crate::relay_targets::{
- AckPolicy, PublishMode, RelayResolutionPolicy, SDK_RELAY_TARGET_MAX_COUNT,
- SdkRelayTargetPolicy, SdkRelayTargetSet, SdkRelayUrlPolicy,
-};
-#[cfg(feature = "runtime")]
pub use crate::runtime::{
BackupReceipt, BackupRequest, IntegrityReceipt, IntegrityRequest, RadrootsClient,
RadrootsClientBuilder, RadrootsSdkClock, RadrootsSdkStorageConfig, RadrootsSdkStoragePaths,
RadrootsSdkTimestamp, RestoreArchive, RestoreReceipt, RestoreRequest, SdkBackupManifest,
SdkBackupManifestKind, SdkBackupState, SdkBackupVerification, SdkEventStoreStorageStatus,
- SdkOutboxStorageStatus, SdkPrivateStoreStorageStatus, SdkPublishTransport, SdkRestoreState,
- SdkSqliteStoreStatus, SdkSqliteWalCheckpointReceipt, SdkSqliteWalStatus, SdkStorageKind,
- StorageCheckpointReceipt, StorageCheckpointRequest, StorageStatusReceipt, StorageStatusRequest,
+ SdkOutboxStorageStatus, SdkPrivateStoreStorageStatus, SdkRestoreState, SdkSqliteStoreStatus,
+ SdkSqliteWalCheckpointReceipt, SdkSqliteWalStatus, SdkStorageKind, StorageCheckpointReceipt,
+ StorageCheckpointRequest, StorageStatusReceipt, StorageStatusRequest,
};
#[cfg(all(feature = "runtime", feature = "signer-adapters"))]
pub use crate::signer_provider::{
@@ -234,6 +229,14 @@ pub use crate::trade_storage::{
SdkTradeProjectionCacheRecord,
};
#[cfg(feature = "runtime")]
+pub use crate::transport::{
+ HybridProfile, NostrProfile, NostrRelayUrlPolicy, ProxyProfile, PublishMode,
+ ReticulumPreviewBehavior, ReticulumPreviewProfile, SDK_TRANSPORT_TARGET_MAX_COUNT,
+ SatisfactionPolicy, TargetPolicy, TargetSet, TransportDeliveryReceipt,
+ TransportDeliveryTargetStatus, TransportKind, TransportOutcome, TransportProfile,
+ TransportReceipt, TransportTargetReceipt,
+};
+#[cfg(feature = "runtime")]
pub use radroots_trade::dvm::RadrootsTradeInventoryBinWitnessDto;
#[cfg(feature = "runtime")]
pub use radroots_trade::validation_receipt::{
diff --git a/crates/sdk/src/listings_runtime.rs b/crates/sdk/src/listings_runtime.rs
@@ -2,8 +2,8 @@
use crate::workflow_runtime::enqueue_configured_signed_workflow;
#[cfg(feature = "runtime")]
use crate::{
- ListingsClient, RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey,
- SdkRelayTargetPolicy, SdkRelayUrlPolicy,
+ ListingsClient, NostrRelayUrlPolicy, RadrootsSdkError, RadrootsSdkTimestamp,
+ SatisfactionPolicy, SdkIdempotencyKey, TargetPolicy,
workflow_runtime::{SdkWorkflowEnqueueRequest, enqueue_signed_workflow},
};
#[cfg(feature = "runtime")]
@@ -68,7 +68,7 @@ pub struct ListingEnqueuePublishRequest {
#[serde(serialize_with = "crate::actor_json::serialize_actor_context")]
pub actor: RadrootsActorContext,
pub document: RadrootsListingDraftDocumentV1,
- pub target_relays: SdkRelayTargetPolicy,
+ pub target_relays: TargetPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -78,7 +78,7 @@ impl ListingEnqueuePublishRequest {
pub fn new(
actor: RadrootsActorContext,
listing: RadrootsListing,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
) -> Self {
Self::from_document(
actor,
@@ -90,7 +90,7 @@ impl ListingEnqueuePublishRequest {
pub fn from_document(
actor: RadrootsActorContext,
document: RadrootsListingDraftDocumentV1,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
) -> Self {
Self {
actor,
@@ -104,13 +104,13 @@ impl ListingEnqueuePublishRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = SdkRelayTargetPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -241,7 +241,7 @@ impl<'sdk> ListingsClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: ListingPublishPlan,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<ListingEnqueueReceipt, RadrootsSdkError> {
let enqueue = enqueue_configured_signed_workflow(
@@ -251,6 +251,7 @@ impl<'sdk> ListingsClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key,
},
)
@@ -262,7 +263,7 @@ impl<'sdk> ListingsClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: ListingPublishPlan,
- target_relays: SdkRelayTargetPolicy,
+ target_relays: TargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<ListingEnqueueReceipt, RadrootsSdkError> {
@@ -273,6 +274,7 @@ impl<'sdk> ListingsClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key,
},
signer,
diff --git a/crates/sdk/src/orders_runtime.rs b/crates/sdk/src/orders_runtime.rs
@@ -8,20 +8,19 @@ use crate::sync_runtime::SyncProjectionRefreshReceipt;
use crate::sync_runtime::{SyncProjectionRefreshRequest, refresh_product_projections_for_sdk};
#[cfg(feature = "signer-adapters")]
use crate::workflow_runtime::enqueue_configured_signed_workflow;
+#[cfg(all(feature = "runtime", test))]
+use crate::{NostrRelayUrlPolicy, workflow_runtime::enqueue_signed_workflow};
#[cfg(any(feature = "signer-adapters", test))]
use crate::{
- AckPolicy, PrivacyPreflightConfirmation, PrivacyPreflightReceipt, ProductSensitivityField,
- PublishMode, PushOutboxReceipt, PushOutboxRequest, RadrootsSdkRecoveryAction,
- RelayResolutionPolicy, SdkIdempotencyKey, SdkMutationState,
- workflow_runtime::SdkWorkflowEnqueueRequest,
+ PrivacyPreflightConfirmation, PrivacyPreflightReceipt, ProductSensitivityField, PublishMode,
+ PushOutboxReceipt, PushOutboxRequest, RadrootsSdkRecoveryAction, SatisfactionPolicy,
+ SdkIdempotencyKey, SdkMutationState, TargetPolicy, workflow_runtime::SdkWorkflowEnqueueRequest,
};
#[cfg(feature = "runtime")]
use crate::{
RadrootsSdkError, RadrootsSdkTimestamp, TradeResyncClient, TradeSellerClient,
TradeValidationReceiptsClient, TradesClient, order,
};
-#[cfg(all(feature = "runtime", test))]
-use crate::{SdkRelayUrlPolicy, workflow_runtime::enqueue_signed_workflow};
#[cfg(feature = "runtime")]
use radroots_authority::RadrootsActorContext;
#[cfg(all(feature = "runtime", test))]
@@ -75,12 +74,6 @@ use radroots_nostr::prelude::{
RadrootsNostrEventId, RadrootsNostrFilter, RadrootsNostrKind, RadrootsNostrPublicKey,
radroots_nostr_filter_tag,
};
-#[cfg(all(feature = "runtime", feature = "relay-runtime"))]
-use radroots_relay_transport::{
- RadrootsNostrClientFetchAdapter, RadrootsRelayFetchAdapter, RadrootsRelayFetchEventReceipt,
- RadrootsRelayFetchOutcomeKind, RadrootsRelayFetchReceipt, RadrootsRelayFetchRelayOutcome,
- RadrootsRelayFetchRequest, RadrootsRelayOutcomeKind, fetch_and_ingest_relay_events,
-};
#[cfg(feature = "runtime")]
use radroots_trade::dvm::RADROOTS_DVM_TAG_VALIDATION_RECEIPT;
#[cfg(feature = "runtime")]
@@ -112,6 +105,12 @@ use radroots_trade::validation_receipt::{
};
#[cfg(feature = "runtime")]
use radroots_trade::workflow::RadrootsTradeWorkflowState;
+#[cfg(all(feature = "runtime", feature = "relay-runtime"))]
+use radroots_transport_nostr::{
+ RadrootsNostrClientFetchAdapter, RadrootsRelayFetchAdapter, RadrootsRelayFetchEventReceipt,
+ RadrootsRelayFetchOutcomeKind, RadrootsRelayFetchReceipt, RadrootsRelayFetchRelayOutcome,
+ RadrootsRelayFetchRequest, RadrootsRelayOutcomeKind, fetch_and_ingest_relay_events,
+};
#[cfg(feature = "runtime")]
use serde::Deserialize;
#[cfg(feature = "runtime")]
@@ -281,9 +280,9 @@ pub struct TradeSubmitEnqueueRequest {
pub actor: RadrootsActorContext,
pub listing_event: RadrootsNostrEventPtr,
pub order: RadrootsOrderRequest,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -295,9 +294,9 @@ impl TradeSubmitEnqueueRequest {
actor: RadrootsActorContext,
listing_event: RadrootsNostrEventPtr,
order: RadrootsOrderRequest,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Self {
Self {
actor,
@@ -314,13 +313,13 @@ impl TradeSubmitEnqueueRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = RelayResolutionPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -487,9 +486,9 @@ pub struct TradeDecisionEnqueueRequest {
pub actor: RadrootsActorContext,
pub request_event: RadrootsNostrEventPtr,
pub decision: RadrootsOrderDecision,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -501,9 +500,9 @@ impl TradeDecisionEnqueueRequest {
actor: RadrootsActorContext,
request_event: RadrootsNostrEventPtr,
decision: RadrootsOrderDecision,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Self {
Self {
actor,
@@ -520,13 +519,13 @@ impl TradeDecisionEnqueueRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = RelayResolutionPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -628,9 +627,9 @@ pub struct TradeRevisionProposalEnqueueRequest {
pub root_event: RadrootsNostrEventPtr,
pub previous_event: RadrootsNostrEventPtr,
pub proposal: RadrootsOrderRevisionProposal,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -643,9 +642,9 @@ impl TradeRevisionProposalEnqueueRequest {
root_event: RadrootsNostrEventPtr,
previous_event: RadrootsNostrEventPtr,
proposal: RadrootsOrderRevisionProposal,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Self {
Self {
actor,
@@ -663,13 +662,13 @@ impl TradeRevisionProposalEnqueueRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = RelayResolutionPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -773,9 +772,9 @@ pub struct TradeRevisionDecisionEnqueueRequest {
pub root_event: RadrootsNostrEventPtr,
pub previous_event: RadrootsNostrEventPtr,
pub decision: RadrootsOrderRevisionDecision,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -788,9 +787,9 @@ impl TradeRevisionDecisionEnqueueRequest {
root_event: RadrootsNostrEventPtr,
previous_event: RadrootsNostrEventPtr,
decision: RadrootsOrderRevisionDecision,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Self {
Self {
actor,
@@ -808,13 +807,13 @@ impl TradeRevisionDecisionEnqueueRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = RelayResolutionPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -918,9 +917,9 @@ pub struct TradeCancellationEnqueueRequest {
pub root_event: RadrootsNostrEventPtr,
pub previous_event: RadrootsNostrEventPtr,
pub cancellation: RadrootsOrderCancellation,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
}
@@ -933,9 +932,9 @@ impl TradeCancellationEnqueueRequest {
root_event: RadrootsNostrEventPtr,
previous_event: RadrootsNostrEventPtr,
cancellation: RadrootsOrderCancellation,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Self {
Self {
actor,
@@ -953,13 +952,13 @@ impl TradeCancellationEnqueueRequest {
pub fn try_with_target_relays<I, S>(
mut self,
target_relays: I,
- policy: SdkRelayUrlPolicy,
+ policy: NostrRelayUrlPolicy,
) -> Result<Self, RadrootsSdkError>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
- self.target_relays = RelayResolutionPolicy::try_explicit(target_relays, policy)?;
+ self.target_relays = TargetPolicy::try_nostr_relays(target_relays, policy)?;
Ok(self)
}
@@ -1069,9 +1068,9 @@ pub struct TradeProposeRequest {
pub items: Vec<RadrootsOrderItem>,
pub economics: RadrootsOrderEconomics,
pub public_note: Option<String>,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub privacy_confirmation: PrivacyPreflightConfirmation,
pub idempotency_key: Option<SdkIdempotencyKey>,
pub created_at: Option<RadrootsSdkTimestamp>,
@@ -1087,9 +1086,9 @@ impl TradeProposeRequest {
seller_pubkey: RadrootsPublicKey,
items: Vec<RadrootsOrderItem>,
economics: RadrootsOrderEconomics,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Self {
Self {
actor,
@@ -1149,9 +1148,9 @@ pub struct TradeAcceptRequest {
pub actor: RadrootsActorContext,
pub locator: RadrootsTradeLocator,
pub inventory_commitments: Vec<RadrootsOrderInventoryCommitment>,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub evidence_mode: TradeEvidenceMode,
pub privacy_confirmation: PrivacyPreflightConfirmation,
pub idempotency_key: Option<SdkIdempotencyKey>,
@@ -1164,9 +1163,9 @@ impl TradeAcceptRequest {
actor: RadrootsActorContext,
locator: RadrootsTradeLocator,
inventory_commitments: Vec<RadrootsOrderInventoryCommitment>,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
evidence_mode: TradeEvidenceMode,
) -> Self {
Self {
@@ -1213,9 +1212,9 @@ pub struct TradeDeclineRequest {
pub actor: RadrootsActorContext,
pub locator: RadrootsTradeLocator,
pub reason: String,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub evidence_mode: TradeEvidenceMode,
pub privacy_confirmation: PrivacyPreflightConfirmation,
pub idempotency_key: Option<SdkIdempotencyKey>,
@@ -1228,9 +1227,9 @@ impl TradeDeclineRequest {
actor: RadrootsActorContext,
locator: RadrootsTradeLocator,
reason: impl Into<String>,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
evidence_mode: TradeEvidenceMode,
) -> Self {
Self {
@@ -1277,9 +1276,9 @@ pub struct TradeCancelRequest {
pub actor: RadrootsActorContext,
pub locator: RadrootsTradeLocator,
pub reason: String,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub evidence_mode: TradeEvidenceMode,
pub privacy_confirmation: PrivacyPreflightConfirmation,
pub idempotency_key: Option<SdkIdempotencyKey>,
@@ -1292,9 +1291,9 @@ impl TradeCancelRequest {
actor: RadrootsActorContext,
locator: RadrootsTradeLocator,
reason: impl Into<String>,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
evidence_mode: TradeEvidenceMode,
) -> Self {
Self {
@@ -1344,9 +1343,9 @@ pub struct TradeRevisionProposalRequest {
pub items: Vec<RadrootsOrderItem>,
pub economics: RadrootsOrderEconomics,
pub reason: String,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub evidence_mode: TradeEvidenceMode,
pub privacy_confirmation: PrivacyPreflightConfirmation,
pub idempotency_key: Option<SdkIdempotencyKey>,
@@ -1362,9 +1361,9 @@ impl TradeRevisionProposalRequest {
items: Vec<RadrootsOrderItem>,
economics: RadrootsOrderEconomics,
reason: impl Into<String>,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
evidence_mode: TradeEvidenceMode,
) -> Self {
Self {
@@ -1415,9 +1414,9 @@ pub struct TradeRevisionDecisionRequest {
pub locator: RadrootsTradeLocator,
pub revision_id: RadrootsOrderRevisionId,
pub decision: RadrootsOrderRevisionOutcome,
- pub target_relays: RelayResolutionPolicy,
+ pub target_relays: TargetPolicy,
pub publish_mode: PublishMode,
- pub ack_policy: AckPolicy,
+ pub ack_policy: SatisfactionPolicy,
pub evidence_mode: TradeEvidenceMode,
pub privacy_confirmation: PrivacyPreflightConfirmation,
pub idempotency_key: Option<SdkIdempotencyKey>,
@@ -1431,9 +1430,9 @@ impl TradeRevisionDecisionRequest {
locator: RadrootsTradeLocator,
revision_id: RadrootsOrderRevisionId,
decision: RadrootsOrderRevisionOutcome,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
evidence_mode: TradeEvidenceMode,
) -> Self {
Self {
@@ -2639,9 +2638,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeSubmitPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<TradeSubmitReceipt, RadrootsSdkError> {
validate_trade_enqueue_policy(publish_mode, ack_policy)?;
@@ -2652,6 +2651,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
)
@@ -2664,9 +2664,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeSubmitPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<TradeSubmitReceipt, RadrootsSdkError> {
@@ -2678,6 +2678,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
signer,
@@ -2776,9 +2777,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeDecisionPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<TradeDecisionReceipt, RadrootsSdkError> {
validate_trade_enqueue_policy(publish_mode, ack_policy)?;
@@ -2795,6 +2796,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
)
@@ -2807,9 +2809,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeDecisionPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<TradeDecisionReceipt, RadrootsSdkError> {
@@ -2827,6 +2829,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
signer,
@@ -2930,9 +2933,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeRevisionProposalPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<TradeRevisionProposalReceipt, RadrootsSdkError> {
validate_trade_enqueue_policy(publish_mode, ack_policy)?;
@@ -2949,6 +2952,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
)
@@ -2961,9 +2965,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeRevisionProposalPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<TradeRevisionProposalReceipt, RadrootsSdkError> {
@@ -2981,6 +2985,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
signer,
@@ -3084,9 +3089,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeRevisionDecisionPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<TradeRevisionDecisionReceipt, RadrootsSdkError> {
validate_trade_enqueue_policy(publish_mode, ack_policy)?;
@@ -3103,6 +3108,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
)
@@ -3115,9 +3121,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeRevisionDecisionPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<TradeRevisionDecisionReceipt, RadrootsSdkError> {
@@ -3135,6 +3141,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
signer,
@@ -3238,9 +3245,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeCancellationPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
) -> Result<TradeCancellationReceipt, RadrootsSdkError> {
validate_trade_enqueue_policy(publish_mode, ack_policy)?;
@@ -3257,6 +3264,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
)
@@ -3269,9 +3277,9 @@ impl<'sdk> TradesClient<'sdk> {
&self,
actor: &RadrootsActorContext,
plan: TradeCancellationPlan,
- target_relays: RelayResolutionPolicy,
+ target_relays: TargetPolicy,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<TradeCancellationReceipt, RadrootsSdkError> {
@@ -3289,6 +3297,7 @@ impl<'sdk> TradesClient<'sdk> {
actor,
frozen_draft: &plan.frozen_draft,
target_relays: target_relays.workflow_target_policy(),
+ satisfaction_policy: ack_policy,
idempotency_key,
},
signer,
@@ -3607,7 +3616,7 @@ async fn execute_trade_resync_with_fetch_adapter<A>(
where
A: RadrootsRelayFetchAdapter,
{
- let relay_targets = sdk.relay_urls().to_vec();
+ let relay_targets = sdk.configured_nostr_relay_urls().to_vec();
if relay_targets.is_empty() {
return Err(RadrootsSdkError::empty_target_relays(operation));
}
@@ -4289,7 +4298,7 @@ fn validation_receipt_relay_targets(
sdk: &crate::RadrootsClient,
operation: impl Into<String>,
) -> Result<Vec<String>, RadrootsSdkError> {
- let relay_targets = sdk.relay_urls().to_vec();
+ let relay_targets = sdk.configured_nostr_relay_urls().to_vec();
if relay_targets.is_empty() {
return Err(RadrootsSdkError::empty_target_relays(operation));
}
@@ -6111,7 +6120,7 @@ fn event_ptr(event_id: &RadrootsEventId) -> RadrootsNostrEventPtr {
async fn trade_product_post_enqueue_outcome<Plan, Receipt>(
sdk: &crate::RadrootsClient,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
outbox_event_id: i64,
receipt: Receipt,
) -> Result<TradeMutationOutcome<Plan, Receipt>, RadrootsSdkError> {
@@ -6132,33 +6141,37 @@ async fn trade_product_post_enqueue_outcome<Plan, Receipt>(
#[cfg(feature = "signer-adapters")]
fn push_request_for_ack_policy(
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
outbox_event_id: i64,
) -> Result<PushOutboxRequest, RadrootsSdkError> {
let request = PushOutboxRequest::new().with_outbox_event_id(outbox_event_id);
match ack_policy {
- AckPolicy::NoWait => Err(RadrootsSdkError::InvalidRequest {
+ SatisfactionPolicy::NoWait => Err(RadrootsSdkError::InvalidRequest {
message: "trade enqueue-and-publish requires a relay acknowledgement policy".to_owned(),
}),
- AckPolicy::AtLeastOneRelay => Ok(request.with_accepted_quorum(1)),
- AckPolicy::AllRelays => Ok(request),
- AckPolicy::Quorum { required } => Ok(request.with_accepted_quorum(usize::from(required))),
+ SatisfactionPolicy::AtLeastOneTarget => Ok(request.with_accepted_quorum(1)),
+ SatisfactionPolicy::AllTargets => Ok(request),
+ SatisfactionPolicy::AtLeast { required } => {
+ Ok(request.with_accepted_quorum(usize::from(required)))
+ }
}
}
#[cfg(feature = "signer-adapters")]
fn validate_trade_product_publish_policy(
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Result<(), RadrootsSdkError> {
match publish_mode {
- PublishMode::DryRun | PublishMode::EnqueueOnly if ack_policy != AckPolicy::NoWait => {
+ PublishMode::DryRun | PublishMode::EnqueueOnly
+ if ack_policy != SatisfactionPolicy::NoWait =>
+ {
Err(RadrootsSdkError::InvalidRequest {
message: "trade dry-run and enqueue-only modes require no-wait acknowledgement"
.to_owned(),
})
}
- PublishMode::EnqueueAndPublish if ack_policy == AckPolicy::NoWait => {
+ PublishMode::EnqueueAndPublish if ack_policy == SatisfactionPolicy::NoWait => {
Err(RadrootsSdkError::InvalidRequest {
message: "trade enqueue-and-publish requires a relay acknowledgement policy"
.to_owned(),
@@ -6264,20 +6277,20 @@ fn trade_reason_contains_private_coordination(reason: &str) -> bool {
#[cfg(any(feature = "signer-adapters", test))]
fn validate_trade_enqueue_policy(
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> Result<(), RadrootsSdkError> {
if publish_mode == PublishMode::DryRun {
return Err(RadrootsSdkError::InvalidRequest {
message: "trade dry-run publish mode must use a prepare request".to_owned(),
});
}
- if publish_mode == PublishMode::EnqueueOnly && ack_policy != AckPolicy::NoWait {
+ if publish_mode == PublishMode::EnqueueOnly && ack_policy != SatisfactionPolicy::NoWait {
return Err(RadrootsSdkError::InvalidRequest {
message: "trade enqueue-only publish mode only supports no-wait acknowledgement"
.to_owned(),
});
}
- if publish_mode == PublishMode::EnqueueAndPublish && ack_policy == AckPolicy::NoWait {
+ if publish_mode == PublishMode::EnqueueAndPublish && ack_policy == SatisfactionPolicy::NoWait {
return Err(RadrootsSdkError::InvalidRequest {
message: "trade enqueue-and-publish requires a relay acknowledgement policy".to_owned(),
});
diff --git a/crates/sdk/src/relay_targets.rs b/crates/sdk/src/relay_targets.rs
@@ -1,280 +0,0 @@
-use crate::RadrootsSdkError;
-use radroots_relay_transport::{RadrootsRelayUrl, RadrootsRelayUrlPolicy};
-use serde::ser::SerializeStruct;
-use std::collections::BTreeSet;
-
-pub const SDK_RELAY_TARGET_MAX_COUNT: usize = 20;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum PublishMode {
- DryRun,
- EnqueueOnly,
- EnqueueAndPublish,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum AckPolicy {
- NoWait,
- AtLeastOneRelay,
- AllRelays,
- Quorum { required: u16 },
-}
-
-impl AckPolicy {
- pub fn quorum(required: u16) -> Result<Self, RadrootsSdkError> {
- if required == 0 {
- return Err(RadrootsSdkError::InvalidRequest {
- message: "ack policy quorum must require at least one relay".to_owned(),
- });
- }
- Ok(Self::Quorum { required })
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum RelayResolutionPolicy {
- ConfiguredRelays,
- Explicit(SdkRelayTargetSet),
-}
-
-impl RelayResolutionPolicy {
- pub fn configured_relays() -> Self {
- Self::ConfiguredRelays
- }
-
- pub fn explicit(targets: SdkRelayTargetSet) -> Self {
- Self::Explicit(targets)
- }
-
- pub fn try_explicit<I, S>(
- relays: I,
- url_policy: SdkRelayUrlPolicy,
- ) -> Result<Self, RadrootsSdkError>
- where
- I: IntoIterator<Item = S>,
- S: AsRef<str>,
- {
- Ok(Self::Explicit(SdkRelayTargetSet::new(relays, url_policy)?))
- }
-
- #[cfg(any(feature = "signer-adapters", test))]
- pub(crate) fn workflow_target_policy(self) -> SdkRelayTargetPolicy {
- match self {
- Self::ConfiguredRelays => SdkRelayTargetPolicy::UseConfiguredRelays,
- Self::Explicit(targets) => SdkRelayTargetPolicy::Explicit(targets),
- }
- }
-}
-
-impl serde::Serialize for RelayResolutionPolicy {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- match self {
- Self::ConfiguredRelays => {
- let mut state = serializer.serialize_struct("RelayResolutionPolicy", 1)?;
- state.serialize_field("kind", "configured_relays")?;
- state.end()
- }
- Self::Explicit(targets) => {
- let mut state = serializer.serialize_struct("RelayResolutionPolicy", 3)?;
- state.serialize_field("kind", "explicit")?;
- state.serialize_field("relays", targets.relays())?;
- state.serialize_field("canonical_relays", targets.canonical_relays())?;
- state.end()
- }
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum SdkRelayUrlPolicy {
- Public,
- Localhost,
-}
-
-impl SdkRelayUrlPolicy {
- pub(crate) fn relay_transport_policy(self) -> RadrootsRelayUrlPolicy {
- match self {
- Self::Public => RadrootsRelayUrlPolicy::Public,
- Self::Localhost => RadrootsRelayUrlPolicy::Localhost,
- }
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum SdkRelayTargetPolicy {
- Explicit(SdkRelayTargetSet),
- UseConfiguredRelays,
- UsePublishTransport,
-}
-
-impl SdkRelayTargetPolicy {
- pub fn explicit(targets: SdkRelayTargetSet) -> Self {
- Self::Explicit(targets)
- }
-
- pub fn try_explicit<I, S>(
- relays: I,
- url_policy: SdkRelayUrlPolicy,
- ) -> Result<Self, RadrootsSdkError>
- where
- I: IntoIterator<Item = S>,
- S: AsRef<str>,
- {
- Ok(Self::Explicit(SdkRelayTargetSet::new(relays, url_policy)?))
- }
-
- pub fn use_publish_transport() -> Self {
- Self::UsePublishTransport
- }
-}
-
-impl serde::Serialize for SdkRelayTargetPolicy {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- match self {
- Self::Explicit(targets) => {
- let mut state = serializer.serialize_struct("SdkRelayTargetPolicy", 3)?;
- state.serialize_field("kind", "explicit")?;
- state.serialize_field("relays", targets.relays())?;
- state.serialize_field("canonical_relays", targets.canonical_relays())?;
- state.end()
- }
- Self::UseConfiguredRelays => {
- let mut state = serializer.serialize_struct("SdkRelayTargetPolicy", 1)?;
- state.serialize_field("kind", "use_configured_relays")?;
- state.end()
- }
- Self::UsePublishTransport => {
- let mut state = serializer.serialize_struct("SdkRelayTargetPolicy", 1)?;
- state.serialize_field("kind", "use_publish_transport")?;
- state.end()
- }
- }
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkRelayTargetSet {
- relays: Vec<String>,
- canonical_relays: Vec<String>,
-}
-
-impl SdkRelayTargetSet {
- pub fn new<I, S>(relays: I, policy: SdkRelayUrlPolicy) -> Result<Self, RadrootsSdkError>
- where
- I: IntoIterator<Item = S>,
- S: AsRef<str>,
- {
- let mut ordered_relays = Vec::new();
- let mut seen = BTreeSet::new();
- for relay in relays {
- let normalized = normalized_relay_url(relay.as_ref(), policy)?;
- if seen.insert(normalized.clone()) {
- ordered_relays.push(normalized);
- }
- }
- Self::from_normalized_ordered(ordered_relays)
- }
-
- pub fn relays(&self) -> &[String] {
- self.relays.as_slice()
- }
-
- pub fn canonical_relays(&self) -> &[String] {
- self.canonical_relays.as_slice()
- }
-
- pub fn into_vec(self) -> Vec<String> {
- self.relays
- }
-
- pub fn len(&self) -> usize {
- self.relays.len()
- }
-
- pub fn is_empty(&self) -> bool {
- self.relays.is_empty()
- }
-
- pub(crate) fn from_configured_relays<I, S>(
- relays: I,
- policy: SdkRelayUrlPolicy,
- ) -> Result<Vec<String>, RadrootsSdkError>
- where
- I: IntoIterator<Item = S>,
- S: AsRef<str>,
- {
- let relays = relays.into_iter().collect::<Vec<_>>();
- if relays.is_empty() {
- return Ok(Vec::new());
- }
- Ok(Self::new(relays, policy)?.into_vec())
- }
-
- pub(crate) fn from_normalized_relays(relays: Vec<String>) -> Result<Self, RadrootsSdkError> {
- let mut ordered = Vec::new();
- let mut seen = BTreeSet::new();
- for relay in relays {
- if seen.insert(relay.clone()) {
- ordered.push(relay);
- }
- }
- Self::from_normalized_ordered(ordered)
- }
-
- fn from_normalized_ordered(relays: Vec<String>) -> Result<Self, RadrootsSdkError> {
- if relays.is_empty() {
- return Err(RadrootsSdkError::empty_target_relays(
- "sdk relay target set",
- ));
- }
- if relays.len() > SDK_RELAY_TARGET_MAX_COUNT {
- return Err(RadrootsSdkError::relay_target_limit_exceeded(
- SDK_RELAY_TARGET_MAX_COUNT,
- relays.len(),
- ));
- }
- let canonical_relays = relays.iter().cloned().collect::<BTreeSet<_>>();
- Ok(Self {
- relays,
- canonical_relays: canonical_relays.into_iter().collect(),
- })
- }
-}
-
-impl serde::Serialize for SdkRelayTargetSet {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- let mut state = serializer.serialize_struct("SdkRelayTargetSet", 2)?;
- state.serialize_field("relays", self.relays())?;
- state.serialize_field("canonical_relays", self.canonical_relays())?;
- state.end()
- }
-}
-
-fn normalized_relay_url(
- value: &str,
- policy: SdkRelayUrlPolicy,
-) -> Result<String, RadrootsSdkError> {
- let relay = RadrootsRelayUrl::parse(value, policy.relay_transport_policy())?;
- Ok(relay.into_string())
-}
-
-#[cfg(test)]
-#[path = "../tests/unit/relay_targets_tests.rs"]
-mod tests;
diff --git a/crates/sdk/src/runtime.rs b/crates/sdk/src/runtime.rs
@@ -3,7 +3,7 @@ use crate::private_store::{SDK_PRIVATE_STORE_SCHEMA_VERSION, SdkPrivateStore};
#[cfg(feature = "runtime")]
use crate::{
DvmClient, FarmsClient, GeoNamesClient, ListingsClient, MarketClient, RadrootsGeoNamesConfig,
- RadrootsSdkError, SdkRelayTargetSet, SdkRelayUrlPolicy, SyncClient, TradesClient,
+ RadrootsSdkError, SyncClient, TradesClient, transport::TransportProfile,
};
#[cfg(all(feature = "runtime", feature = "signer-adapters"))]
use crate::{
@@ -24,9 +24,6 @@ use std::{
time::{SystemTime, UNIX_EPOCH},
};
-#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
-use crate::adapters::radrootsd::RadrootsdProxyConfig;
-
#[cfg(feature = "runtime")]
const SDK_STORAGE_MANIFEST_VERSION: u16 = 1;
#[cfg(feature = "runtime")]
@@ -395,33 +392,6 @@ pub enum SdkRestoreState {
}
#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum SdkPublishTransport {
- DirectNostrRelay,
- #[cfg(feature = "radrootsd-proxy")]
- RadrootsdProxy(RadrootsdProxyConfig),
-}
-
-#[cfg(feature = "runtime")]
-impl Default for SdkPublishTransport {
- fn default() -> Self {
- Self::DirectNostrRelay
- }
-}
-
-#[cfg(feature = "runtime")]
-impl SdkPublishTransport {
- pub(crate) fn supports_delegated_relay_resolution(&self) -> bool {
- match self {
- Self::DirectNostrRelay => false,
- #[cfg(feature = "radrootsd-proxy")]
- Self::RadrootsdProxy(_) => true,
- }
- }
-}
-
-#[cfg(feature = "runtime")]
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct RestoreArchive {
pub source: PathBuf,
@@ -455,9 +425,7 @@ pub struct RadrootsClientBuilder {
storage: RadrootsSdkStorageConfig,
geonames: Option<RadrootsGeoNamesConfig>,
clock: RadrootsSdkClock,
- relay_urls: Vec<String>,
- relay_url_policy: SdkRelayUrlPolicy,
- publish_transport: SdkPublishTransport,
+ transport_profile: TransportProfile,
#[cfg(feature = "signer-adapters")]
signer_provider: Option<RadrootsSdkSignerProvider>,
}
@@ -469,9 +437,7 @@ impl Default for RadrootsClientBuilder {
storage: RadrootsSdkStorageConfig::Memory,
geonames: None,
clock: RadrootsSdkClock::System,
- relay_urls: Vec::new(),
- relay_url_policy: SdkRelayUrlPolicy::Public,
- publish_transport: SdkPublishTransport::DirectNostrRelay,
+ transport_profile: TransportProfile::default(),
#[cfg(feature = "signer-adapters")]
signer_provider: None,
}
@@ -510,18 +476,8 @@ impl RadrootsClientBuilder {
self
}
- pub fn relay_url(mut self, relay_url: impl Into<String>) -> Self {
- self.relay_urls.push(relay_url.into());
- self
- }
-
- pub fn relay_url_policy(mut self, policy: SdkRelayUrlPolicy) -> Self {
- self.relay_url_policy = policy;
- self
- }
-
- pub fn publish_transport(mut self, transport: SdkPublishTransport) -> Self {
- self.publish_transport = transport;
+ pub fn transport_profile(mut self, profile: TransportProfile) -> Self {
+ self.transport_profile = profile;
self
}
@@ -533,8 +489,6 @@ impl RadrootsClientBuilder {
pub async fn build(self) -> Result<RadrootsClient, RadrootsSdkError> {
let storage = open_storage(&self.storage).await?;
- let relay_urls =
- SdkRelayTargetSet::from_configured_relays(&self.relay_urls, self.relay_url_policy)?;
Ok(RadrootsClient {
_event_store: storage.event_store,
_outbox: storage.outbox,
@@ -542,8 +496,7 @@ impl RadrootsClientBuilder {
storage_paths: storage.paths,
geonames: self.geonames,
clock: self.clock,
- relay_urls,
- publish_transport: self.publish_transport,
+ transport_profile: self.transport_profile,
#[cfg(feature = "signer-adapters")]
signer_provider: self.signer_provider,
})
@@ -559,8 +512,7 @@ pub struct RadrootsClient {
storage_paths: Option<RadrootsSdkStoragePaths>,
geonames: Option<RadrootsGeoNamesConfig>,
clock: RadrootsSdkClock,
- relay_urls: Vec<String>,
- publish_transport: SdkPublishTransport,
+ transport_profile: TransportProfile,
#[cfg(feature = "signer-adapters")]
signer_provider: Option<RadrootsSdkSignerProvider>,
}
@@ -603,12 +555,12 @@ impl RadrootsClient {
self.clock.now()
}
- pub fn relay_urls(&self) -> &[String] {
- &self.relay_urls
+ pub fn transport_profile(&self) -> &TransportProfile {
+ &self.transport_profile
}
- pub fn publish_transport(&self) -> &SdkPublishTransport {
- &self.publish_transport
+ pub fn configured_nostr_relay_urls(&self) -> Vec<String> {
+ self.transport_profile.configured_nostr_relay_urls()
}
#[cfg(feature = "signer-adapters")]
@@ -664,7 +616,7 @@ impl RadrootsClient {
store: event_store_status,
total_events: event_summary.total_events,
projection_eligible_events: event_summary.projection_eligible_events,
- relay_observations: event_summary.relay_observations,
+ relay_observations: event_summary.transport_observations,
last_event_seq: event_summary.last_event_seq,
last_event_updated_at_ms: event_summary.last_event_updated_at_ms,
},
diff --git a/crates/sdk/src/sync_runtime.rs b/crates/sdk/src/sync_runtime.rs
@@ -1,11 +1,13 @@
#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
use crate::adapters::radrootsd::{
- RadrootsdError, RadrootsdProxyPublishAdapter, RadrootsdProxyPublishRequest,
+ RadrootsdError, RadrootsdProxyConfig, RadrootsdProxyPublishAdapter,
+ RadrootsdProxyPublishRequest,
};
#[cfg(feature = "runtime")]
use crate::{
- RadrootsSdkError, SdkRelayUrlPolicy, SyncClient,
+ NostrRelayUrlPolicy, RadrootsSdkError, SyncClient,
runtime::{RadrootsClient, sdk_now_ms},
+ transport::TransportProfile,
};
#[cfg(feature = "runtime")]
use radroots_event_store::{RADROOTS_EVENT_STORE_QUERY_LIMIT_MAX, RadrootsEventStoreStatusSummary};
@@ -14,24 +16,29 @@ use radroots_events::ids::RadrootsEventId;
#[cfg(all(feature = "runtime", feature = "relay-runtime"))]
use radroots_nostr::prelude::RadrootsNostrClient;
#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
-use radroots_outbox::RadrootsOutboxClaimedEvent;
+use radroots_outbox::{
+ RadrootsOutboxClaimedEvent, RadrootsOutboxDeliveryTargetRecord,
+ RadrootsOutboxDeliveryTargetStatus,
+};
#[cfg(feature = "runtime")]
use radroots_outbox::{RadrootsOutboxEventState, RadrootsOutboxStatusSummary};
#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
use radroots_publish_proxy_protocol::PublishDeliveryPolicy;
-#[cfg(all(feature = "runtime", feature = "relay-runtime"))]
-use radroots_relay_transport::RadrootsNostrClientPublishAdapter;
-#[cfg(feature = "runtime")]
-use radroots_relay_transport::{
- RadrootsOutboxPublishPolicy, RadrootsRelayOutcomeKind, RadrootsRelayPublishAdapter,
- RadrootsRelayPublishReceipt, RadrootsRelayPublishRelayReceipt, publish_claimed_outbox_event,
-};
#[cfg(feature = "runtime")]
use radroots_trade::projection::{
RADROOTS_PRODUCT_PROJECTION_ID, RADROOTS_PRODUCT_PROJECTION_VERSION,
RadrootsProjectionRefreshReceipt, RadrootsProjectionRefreshRequest,
refresh_product_projections,
};
+#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
+use radroots_transport::{RadrootsTransportKind, RadrootsTransportSatisfactionPolicy};
+#[cfg(all(feature = "runtime", feature = "relay-runtime"))]
+use radroots_transport_nostr::RadrootsNostrClientPublishAdapter;
+#[cfg(feature = "runtime")]
+use radroots_transport_nostr::{
+ RadrootsOutboxPublishPolicy, RadrootsRelayOutcomeKind, RadrootsRelayPublishAdapter,
+ RadrootsRelayPublishReceipt, RadrootsRelayPublishRelayReceipt, publish_claimed_outbox_event,
+};
#[cfg(feature = "runtime")]
pub const PUSH_OUTBOX_DEFAULT_LIMIT: usize = 20;
@@ -95,7 +102,7 @@ impl From<RadrootsEventStoreStatusSummary> for SyncEventStoreStatus {
Self {
total_events: summary.total_events,
projection_eligible_events: summary.projection_eligible_events,
- relay_observations: summary.relay_observations,
+ relay_observations: summary.transport_observations,
last_event_seq: summary.last_event_seq,
last_event_updated_at_ms: summary.last_event_updated_at_ms,
}
@@ -193,7 +200,7 @@ impl SyncProjectionRefreshReceipt {
listing_upserts: receipt.listing_upserts,
trade_upserts: receipt.trade_upserts,
validation_receipts: receipt.validation_receipts,
- relay_observations: receipt.relay_observations,
+ relay_observations: receipt.transport_observations,
last_event_seq: receipt.last_event_seq,
}
}
@@ -222,7 +229,7 @@ pub struct PushOutboxRequest {
pub outbox_event_id: Option<i64>,
pub republish_accepted_relays: bool,
pub accepted_quorum: Option<usize>,
- pub relay_url_policy: SdkRelayUrlPolicy,
+ pub relay_url_policy: NostrRelayUrlPolicy,
pub auth_policy: SdkRelayAuthPolicy,
pub claim_ttl_ms: i64,
pub next_attempt_delay_ms: i64,
@@ -236,7 +243,7 @@ impl Default for PushOutboxRequest {
outbox_event_id: None,
republish_accepted_relays: false,
accepted_quorum: None,
- relay_url_policy: SdkRelayUrlPolicy::Public,
+ relay_url_policy: NostrRelayUrlPolicy::Public,
auth_policy: SdkRelayAuthPolicy::DetectOnly,
claim_ttl_ms: PUSH_OUTBOX_DEFAULT_CLAIM_TTL_MS,
next_attempt_delay_ms: PUSH_OUTBOX_DEFAULT_NEXT_ATTEMPT_DELAY_MS,
@@ -271,7 +278,7 @@ impl PushOutboxRequest {
self
}
- pub fn with_relay_url_policy(mut self, policy: SdkRelayUrlPolicy) -> Self {
+ pub fn with_relay_url_policy(mut self, policy: NostrRelayUrlPolicy) -> Self {
self.relay_url_policy = policy;
self
}
@@ -480,8 +487,8 @@ impl<'sdk> SyncClient<'sdk> {
event_store: event_store.into(),
outbox: outbox.into(),
relay_targets: SyncRelayTargetSummary {
- configured_count: self.sdk.relay_urls().len(),
- configured_relays: self.sdk.relay_urls().to_vec(),
+ configured_count: self.sdk.configured_nostr_relay_urls().len(),
+ configured_relays: self.sdk.configured_nostr_relay_urls(),
},
})
}
@@ -490,8 +497,8 @@ impl<'sdk> SyncClient<'sdk> {
&self,
request: PushOutboxRequest,
) -> Result<PushOutboxReceipt, RadrootsSdkError> {
- match self.sdk.publish_transport() {
- crate::runtime::SdkPublishTransport::DirectNostrRelay => {
+ match self.sdk.transport_profile() {
+ TransportProfile::Nostr { .. } | TransportProfile::Hybrid { .. } => {
#[cfg(feature = "relay-runtime")]
{
let adapter = RadrootsNostrClientPublishAdapter::new(
@@ -510,13 +517,34 @@ impl<'sdk> SyncClient<'sdk> {
}
}
#[cfg(feature = "radrootsd-proxy")]
- crate::runtime::SdkPublishTransport::RadrootsdProxy(config) => {
- let adapter = RadrootsdProxyPublishAdapter::new(config.clone());
+ TransportProfile::Proxy { profile } => {
+ let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(
+ profile.endpoint_url().to_owned(),
+ ));
self.push_outbox_with_proxy_adapter(&adapter, request).await
}
+ TransportProfile::LocalOnly | TransportProfile::ReticulumPreview { .. } => {
+ if self.push_outbox_has_no_ready_signed_work(&request).await? {
+ return Ok(PushOutboxReceipt::default());
+ }
+ Err(RadrootsSdkError::ProductSyncUnsupported {
+ operation: "sync.push_outbox",
+ required_feature: "transport profile with Nostr delivery",
+ })
+ }
}
}
+ async fn push_outbox_has_no_ready_signed_work(
+ &self,
+ request: &PushOutboxRequest,
+ ) -> Result<bool, RadrootsSdkError> {
+ request.validate()?;
+ let now_ms = sdk_now_ms(self.sdk)?;
+ let summary = self.sdk._outbox.status_summary(now_ms).await?;
+ Ok(summary.ready_signed_events == 0)
+ }
+
pub async fn push_outbox_with_adapter<A>(
&self,
adapter: &A,
@@ -545,11 +573,7 @@ impl<'sdk> SyncClient<'sdk> {
publish_now_ms.saturating_add(request.next_attempt_delay_ms),
)
.republish_accepted_relays(request.republish_accepted_relays)
- .relay_url_policy(request.relay_url_policy.relay_transport_policy());
- let policy = match request.accepted_quorum {
- Some(accepted_quorum) => policy.with_accepted_quorum(accepted_quorum),
- None => policy,
- };
+ .relay_url_policy(request.relay_url_policy.nostr_transport_policy());
let publish = publish_claimed_outbox_event(
&self.sdk._outbox,
&self.sdk._event_store,
@@ -594,7 +618,6 @@ impl<'sdk> SyncClient<'sdk> {
self,
adapter,
&claimed,
- request.accepted_quorum,
request.next_attempt_delay_ms,
publish_now_ms,
)
@@ -663,12 +686,11 @@ async fn push_proxy_claimed_outbox_event(
sync: &SyncClient<'_>,
adapter: &RadrootsdProxyPublishAdapter,
claimed: &RadrootsOutboxClaimedEvent,
- accepted_quorum: Option<usize>,
next_attempt_delay_ms: i64,
now_ms: i64,
) -> Result<RadrootsRelayPublishReceipt, RadrootsSdkError> {
let signed_event = claimed.signed_event.clone().ok_or(
- radroots_relay_transport::RadrootsRelayTransportError::MissingSignedOutboxEvent(
+ radroots_transport_nostr::RadrootsRelayTransportError::MissingSignedOutboxEvent(
claimed.outbox_event_id,
),
)?;
@@ -681,10 +703,14 @@ async fn push_proxy_claimed_outbox_event(
now_ms,
)
.await?;
+ let relays = proxy_nostr_relay_targets(claimed)
+ .iter()
+ .map(|target| target.endpoint_uri.as_str().to_owned())
+ .collect::<Vec<_>>();
let request = RadrootsdProxyPublishRequest {
signed_event: signed_event.clone(),
- relays: claimed.target_relays.clone(),
- delivery_policy: proxy_delivery_policy(claimed.target_relays.len(), accepted_quorum),
+ delivery_policy: proxy_delivery_policy(sync, claimed, relays.len()).await?,
+ relays,
idempotency_key: Some(proxy_outbox_idempotency_key(
claimed.outbox_event_id,
claimed.attempt_count,
@@ -714,18 +740,50 @@ async fn push_proxy_claimed_outbox_event(
}
#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
-fn proxy_delivery_policy(
+async fn proxy_delivery_policy(
+ sync: &SyncClient<'_>,
+ claimed: &RadrootsOutboxClaimedEvent,
target_count: usize,
- accepted_quorum: Option<usize>,
-) -> PublishDeliveryPolicy {
- if let Some(quorum) = accepted_quorum {
- return PublishDeliveryPolicy::Quorum { quorum };
- }
+) -> Result<PublishDeliveryPolicy, RadrootsSdkError> {
+ let plans = sync
+ .sdk
+ ._outbox
+ .delivery_plans(claimed.outbox_event_id)
+ .await?;
+ let plan = plans
+ .iter()
+ .find(|plan| {
+ claimed
+ .delivery_targets
+ .iter()
+ .any(|target| target.delivery_plan_id == plan.delivery_plan_id)
+ })
+ .or_else(|| plans.first())
+ .ok_or_else(|| RadrootsSdkError::InvalidRequest {
+ message: format!(
+ "outbox event {} has no delivery plan for proxy publish",
+ claimed.outbox_event_id
+ ),
+ })?;
+ proxy_delivery_policy_from_satisfaction(target_count, &plan.satisfaction_policy)
+}
+
+#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
+fn proxy_delivery_policy_from_satisfaction(
+ target_count: usize,
+ satisfaction_policy: &RadrootsTransportSatisfactionPolicy,
+) -> Result<PublishDeliveryPolicy, RadrootsSdkError> {
if target_count == 0 {
- PublishDeliveryPolicy::Any
- } else {
- PublishDeliveryPolicy::All
- }
+ return Ok(PublishDeliveryPolicy::Any);
+ }
+ let required = satisfaction_policy.required_target_count(target_count)?;
+ Ok(match satisfaction_policy {
+ RadrootsTransportSatisfactionPolicy::AnyTarget => PublishDeliveryPolicy::Any,
+ RadrootsTransportSatisfactionPolicy::AllTargets => PublishDeliveryPolicy::All,
+ RadrootsTransportSatisfactionPolicy::AtLeast(_) => {
+ PublishDeliveryPolicy::Quorum { quorum: required }
+ }
+ })
}
#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
@@ -745,44 +803,140 @@ async fn complete_proxy_publish_attempt(
next_attempt_delay_ms: i64,
now_ms: i64,
) -> Result<(), RadrootsSdkError> {
- if publish.quorum_met {
+ let mut completed_target_ids = std::collections::BTreeSet::new();
+ for relay in &publish.relays {
+ if let Some(target) = proxy_nostr_relay_targets(claimed)
+ .into_iter()
+ .find(|target| target.endpoint_uri.as_str() == relay.relay_url.as_str())
+ {
+ complete_proxy_delivery_target(sync, claimed, target, relay, now_ms).await?;
+ completed_target_ids.insert(target.delivery_target_id);
+ }
+ }
+ for target in claimed
+ .delivery_targets
+ .iter()
+ .filter(|target| target.status.is_ready_for_attempt())
+ .filter(|target| !completed_target_ids.contains(&target.delivery_target_id))
+ {
+ complete_unmatched_proxy_delivery_target(sync, claimed, target, publish, now_ms).await?;
+ }
+ sync.sdk
+ ._outbox
+ .complete_publish_attempt(
+ claimed.outbox_event_id,
+ claimed.claim_token.as_str(),
+ "radrootsd proxy publish incomplete",
+ "radrootsd proxy publish terminal",
+ now_ms.saturating_add(next_attempt_delay_ms),
+ now_ms,
+ )
+ .await?;
+ Ok(())
+}
+
+#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
+fn proxy_nostr_relay_targets(
+ claimed: &RadrootsOutboxClaimedEvent,
+) -> Vec<&RadrootsOutboxDeliveryTargetRecord> {
+ claimed
+ .delivery_targets
+ .iter()
+ .filter(|target| target.transport_kind == RadrootsTransportKind::Nostr)
+ .filter(|target| target.status.is_ready_for_attempt())
+ .collect()
+}
+
+#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
+async fn complete_proxy_delivery_target(
+ sync: &SyncClient<'_>,
+ claimed: &RadrootsOutboxClaimedEvent,
+ target: &RadrootsOutboxDeliveryTargetRecord,
+ relay: &RadrootsRelayPublishRelayReceipt,
+ now_ms: i64,
+) -> Result<(), RadrootsSdkError> {
+ if relay.outcome.counts_toward_quorum() {
sync.sdk
._outbox
- .set_publish_quorum(
+ .mark_delivery_target_accepted(
claimed.outbox_event_id,
claimed.claim_token.as_str(),
- 0,
+ target.delivery_target_id,
now_ms,
)
.await?;
+ } else if relay.outcome.is_retryable() {
sync.sdk
._outbox
- .complete_publish_attempt(
+ .mark_delivery_target_failed_retryable(
claimed.outbox_event_id,
claimed.claim_token.as_str(),
- "radrootsd proxy publish incomplete",
- "radrootsd proxy publish terminal",
- now_ms.saturating_add(next_attempt_delay_ms),
+ target.delivery_target_id,
+ relay
+ .outcome
+ .message
+ .as_deref()
+ .unwrap_or("radrootsd proxy publish retryable"),
now_ms,
)
.await?;
- } else if publish.retryable_count > 0 {
+ } else {
+ sync.sdk
+ ._outbox
+ .mark_delivery_target_failed_terminal(
+ claimed.outbox_event_id,
+ claimed.claim_token.as_str(),
+ target.delivery_target_id,
+ relay
+ .outcome
+ .message
+ .as_deref()
+ .unwrap_or("radrootsd proxy publish terminal"),
+ now_ms,
+ )
+ .await?;
+ }
+ Ok(())
+}
+
+#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))]
+async fn complete_unmatched_proxy_delivery_target(
+ sync: &SyncClient<'_>,
+ claimed: &RadrootsOutboxClaimedEvent,
+ target: &RadrootsOutboxDeliveryTargetRecord,
+ publish: &RadrootsRelayPublishReceipt,
+ now_ms: i64,
+) -> Result<(), RadrootsSdkError> {
+ if publish.quorum_met {
+ sync.sdk
+ ._outbox
+ .mark_delivery_target_accepted(
+ claimed.outbox_event_id,
+ claimed.claim_token.as_str(),
+ target.delivery_target_id,
+ now_ms,
+ )
+ .await?;
+ } else if publish.retryable_count > 0
+ || target.status == RadrootsOutboxDeliveryTargetStatus::FailedRetryable
+ {
sync.sdk
._outbox
- .mark_publish_retryable(
+ .mark_delivery_target_failed_retryable(
claimed.outbox_event_id,
claimed.claim_token.as_str(),
+ target.delivery_target_id,
"radrootsd proxy publish incomplete",
- now_ms.saturating_add(next_attempt_delay_ms),
now_ms,
)
.await?;
} else {
sync.sdk
._outbox
- .mark_publish_failed_terminal(
+ .mark_delivery_target_failed_terminal(
claimed.outbox_event_id,
claimed.claim_token.as_str(),
+ target.delivery_target_id,
"radrootsd proxy publish terminal",
now_ms,
)
diff --git a/crates/sdk/src/transport.rs b/crates/sdk/src/transport.rs
@@ -0,0 +1,494 @@
+use crate::RadrootsSdkError;
+use radroots_transport::{
+ RadrootsTransportDeliveryReceipt, RadrootsTransportKind, RadrootsTransportTarget,
+ RadrootsTransportTargetFingerprint, RadrootsTransportTargetReceipt, RadrootsTransportTargetSet,
+};
+use radroots_transport_nostr::{RadrootsRelayUrl, RadrootsRelayUrlPolicy};
+use serde::ser::SerializeStruct;
+use std::collections::BTreeSet;
+
+pub use radroots_transport::{
+ RadrootsTransportDeliveryReceipt as TransportDeliveryReceipt,
+ RadrootsTransportDeliveryTargetStatus as TransportDeliveryTargetStatus,
+ RadrootsTransportKind as TransportKind, RadrootsTransportOutcome as TransportOutcome,
+ RadrootsTransportTargetReceipt as TransportTargetReceipt,
+};
+
+pub const SDK_TRANSPORT_TARGET_MAX_COUNT: usize = 20;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum PublishMode {
+ DryRun,
+ EnqueueOnly,
+ EnqueueAndPublish,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum SatisfactionPolicy {
+ NoWait,
+ AtLeastOneTarget,
+ AllTargets,
+ AtLeast { required: u16 },
+}
+
+impl SatisfactionPolicy {
+ pub fn at_least(required: u16) -> Result<Self, RadrootsSdkError> {
+ if required == 0 {
+ return Err(RadrootsSdkError::InvalidRequest {
+ message: "satisfaction policy must require at least one target".to_owned(),
+ });
+ }
+ Ok(Self::AtLeast { required })
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum NostrRelayUrlPolicy {
+ Public,
+ Localhost,
+}
+
+impl NostrRelayUrlPolicy {
+ pub(crate) fn nostr_transport_policy(self) -> RadrootsRelayUrlPolicy {
+ match self {
+ Self::Public => RadrootsRelayUrlPolicy::Public,
+ Self::Localhost => RadrootsRelayUrlPolicy::Localhost,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum TargetPolicy {
+ UseConfiguredProfile,
+ Explicit(TargetSet),
+ UseTransportProfile,
+}
+
+impl TargetPolicy {
+ pub fn configured_profile() -> Self {
+ Self::UseConfiguredProfile
+ }
+
+ pub fn explicit(targets: TargetSet) -> Self {
+ Self::Explicit(targets)
+ }
+
+ pub fn try_nostr_relays<I, S>(
+ relays: I,
+ url_policy: NostrRelayUrlPolicy,
+ ) -> Result<Self, RadrootsSdkError>
+ where
+ I: IntoIterator<Item = S>,
+ S: AsRef<str>,
+ {
+ Ok(Self::Explicit(TargetSet::nostr_relays(relays, url_policy)?))
+ }
+
+ pub fn use_transport_profile() -> Self {
+ Self::UseTransportProfile
+ }
+
+ #[cfg(any(feature = "signer-adapters", test))]
+ pub(crate) fn workflow_target_policy(self) -> Self {
+ self
+ }
+}
+
+impl serde::Serialize for TargetPolicy {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ match self {
+ Self::UseConfiguredProfile => {
+ let mut state = serializer.serialize_struct("TargetPolicy", 1)?;
+ state.serialize_field("kind", "use_configured_profile")?;
+ state.end()
+ }
+ Self::Explicit(targets) => {
+ let mut state = serializer.serialize_struct("TargetPolicy", 3)?;
+ state.serialize_field("kind", "explicit")?;
+ state.serialize_field("targets", targets.targets())?;
+ state.serialize_field("canonical_targets", targets.canonical_targets())?;
+ state.end()
+ }
+ Self::UseTransportProfile => {
+ let mut state = serializer.serialize_struct("TargetPolicy", 1)?;
+ state.serialize_field("kind", "use_transport_profile")?;
+ state.end()
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TargetSet {
+ targets: Vec<RadrootsTransportTarget>,
+ canonical_targets: Vec<String>,
+}
+
+impl TargetSet {
+ pub fn new<I, S>(relays: I, policy: NostrRelayUrlPolicy) -> Result<Self, RadrootsSdkError>
+ where
+ I: IntoIterator<Item = S>,
+ S: AsRef<str>,
+ {
+ let mut targets = Vec::new();
+ let mut seen = BTreeSet::new();
+ for relay in relays {
+ let normalized = normalized_nostr_relay_url(relay.as_ref(), policy)?;
+ if seen.insert(normalized.clone()) {
+ targets.push(RadrootsTransportTarget::new(
+ RadrootsTransportKind::Nostr,
+ normalized,
+ )?);
+ }
+ }
+ Self::from_transport_targets(targets)
+ }
+
+ pub fn nostr_relays<I, S>(
+ relays: I,
+ policy: NostrRelayUrlPolicy,
+ ) -> Result<Self, RadrootsSdkError>
+ where
+ I: IntoIterator<Item = S>,
+ S: AsRef<str>,
+ {
+ Self::new(relays, policy)
+ }
+
+ pub fn transport_targets(
+ targets: Vec<RadrootsTransportTarget>,
+ ) -> Result<Self, RadrootsSdkError> {
+ Self::from_transport_targets(targets)
+ }
+
+ pub fn targets(&self) -> &[RadrootsTransportTarget] {
+ self.targets.as_slice()
+ }
+
+ pub fn canonical_targets(&self) -> &[String] {
+ self.canonical_targets.as_slice()
+ }
+
+ pub fn transport_target_set(&self) -> Result<RadrootsTransportTargetSet, RadrootsSdkError> {
+ Ok(RadrootsTransportTargetSet::new(self.targets.clone())?)
+ }
+
+ pub fn nostr_relay_urls(&self) -> Vec<String> {
+ self.targets
+ .iter()
+ .filter(|target| target.kind == RadrootsTransportKind::Nostr)
+ .map(|target| target.uri.as_str().to_owned())
+ .collect()
+ }
+
+ pub fn into_targets(self) -> Vec<RadrootsTransportTarget> {
+ self.targets
+ }
+
+ pub fn len(&self) -> usize {
+ self.targets.len()
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.targets.is_empty()
+ }
+
+ pub(crate) fn from_normalized_nostr_relays(
+ relays: Vec<String>,
+ ) -> Result<Self, RadrootsSdkError> {
+ let mut targets = Vec::new();
+ let mut seen = BTreeSet::new();
+ for relay in relays {
+ if seen.insert(relay.clone()) {
+ targets.push(RadrootsTransportTarget::new(
+ RadrootsTransportKind::Nostr,
+ relay,
+ )?);
+ }
+ }
+ Self::from_transport_targets(targets)
+ }
+
+ fn from_transport_targets(
+ targets: Vec<RadrootsTransportTarget>,
+ ) -> Result<Self, RadrootsSdkError> {
+ if targets.is_empty() {
+ return Err(RadrootsSdkError::empty_target_relays(
+ "sdk transport target set",
+ ));
+ }
+ if targets.len() > SDK_TRANSPORT_TARGET_MAX_COUNT {
+ return Err(RadrootsSdkError::relay_target_limit_exceeded(
+ SDK_TRANSPORT_TARGET_MAX_COUNT,
+ targets.len(),
+ ));
+ }
+ let canonical_targets = targets
+ .iter()
+ .map(|target| {
+ RadrootsTransportTargetFingerprint::from_target(&target.kind, &target.uri)
+ .to_string()
+ })
+ .collect::<BTreeSet<_>>()
+ .into_iter()
+ .collect();
+ Ok(Self {
+ targets,
+ canonical_targets,
+ })
+ }
+}
+
+impl serde::Serialize for TargetSet {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ let mut state = serializer.serialize_struct("TargetSet", 2)?;
+ state.serialize_field("targets", self.targets())?;
+ state.serialize_field("canonical_targets", self.canonical_targets())?;
+ state.end()
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct NostrProfile {
+ target_set: TargetSet,
+}
+
+impl NostrProfile {
+ pub fn new<I, S>(relays: I, policy: NostrRelayUrlPolicy) -> Result<Self, RadrootsSdkError>
+ where
+ I: IntoIterator<Item = S>,
+ S: AsRef<str>,
+ {
+ Ok(Self {
+ target_set: TargetSet::nostr_relays(relays, policy)?,
+ })
+ }
+
+ pub fn target_set(&self) -> &TargetSet {
+ &self.target_set
+ }
+
+ pub fn relay_urls(&self) -> Vec<String> {
+ self.target_set.nostr_relay_urls()
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct ReticulumPreviewProfile {
+ endpoint_uri: String,
+ behavior: ReticulumPreviewBehavior,
+}
+
+impl ReticulumPreviewProfile {
+ pub fn preview_unavailable() -> Self {
+ Self {
+ endpoint_uri: "reticulum:preview".to_owned(),
+ behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts,
+ }
+ }
+
+ pub fn with_behavior(mut self, behavior: ReticulumPreviewBehavior) -> Self {
+ self.behavior = behavior;
+ self
+ }
+
+ pub fn endpoint_uri(&self) -> &str {
+ self.endpoint_uri.as_str()
+ }
+
+ pub fn behavior(&self) -> ReticulumPreviewBehavior {
+ self.behavior
+ }
+
+ pub fn target_set(&self) -> Result<TargetSet, RadrootsSdkError> {
+ TargetSet::transport_targets(vec![RadrootsTransportTarget::new(
+ RadrootsTransportKind::Reticulum,
+ self.endpoint_uri.as_str(),
+ )?])
+ }
+}
+
+impl Default for ReticulumPreviewProfile {
+ fn default() -> Self {
+ Self::preview_unavailable()
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum ReticulumPreviewBehavior {
+ RejectDeliveryAttempts,
+ DeferDeliveryPlans,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct HybridProfile {
+ nostr: NostrProfile,
+ reticulum_preview: ReticulumPreviewProfile,
+}
+
+impl HybridProfile {
+ pub fn new(nostr: NostrProfile, reticulum_preview: ReticulumPreviewProfile) -> Self {
+ Self {
+ nostr,
+ reticulum_preview,
+ }
+ }
+
+ pub fn nostr(&self) -> &NostrProfile {
+ &self.nostr
+ }
+
+ pub fn reticulum_preview(&self) -> &ReticulumPreviewProfile {
+ &self.reticulum_preview
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct ProxyProfile {
+ endpoint_url: String,
+}
+
+impl ProxyProfile {
+ pub fn new(endpoint_url: impl Into<String>) -> Self {
+ Self {
+ endpoint_url: endpoint_url.into(),
+ }
+ }
+
+ pub fn endpoint_url(&self) -> &str {
+ self.endpoint_url.as_str()
+ }
+
+ pub(crate) fn target_set(&self) -> Result<TargetSet, RadrootsSdkError> {
+ TargetSet::transport_targets(vec![RadrootsTransportTarget::new(
+ RadrootsTransportKind::custom("radrootsd_proxy")?,
+ self.endpoint_url.as_str(),
+ )?])
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case", tag = "kind")]
+#[non_exhaustive]
+pub enum TransportProfile {
+ LocalOnly,
+ Nostr { profile: NostrProfile },
+ ReticulumPreview { profile: ReticulumPreviewProfile },
+ Hybrid { profile: HybridProfile },
+ Proxy { profile: ProxyProfile },
+}
+
+impl Default for TransportProfile {
+ fn default() -> Self {
+ Self::LocalOnly
+ }
+}
+
+impl TransportProfile {
+ pub fn local_only() -> Self {
+ Self::LocalOnly
+ }
+
+ pub fn nostr(profile: NostrProfile) -> Self {
+ Self::Nostr { profile }
+ }
+
+ pub fn reticulum_preview(profile: ReticulumPreviewProfile) -> Self {
+ Self::ReticulumPreview { profile }
+ }
+
+ pub fn hybrid(profile: HybridProfile) -> Self {
+ Self::Hybrid { profile }
+ }
+
+ pub fn proxy(profile: ProxyProfile) -> Self {
+ Self::Proxy { profile }
+ }
+
+ pub fn supports_delegated_target_resolution(&self) -> bool {
+ matches!(self, Self::Proxy { .. })
+ }
+
+ pub(crate) fn transport_profile_id(&self) -> &'static str {
+ match self {
+ Self::LocalOnly => "local_only",
+ Self::Nostr { .. } => "nostr",
+ Self::ReticulumPreview { .. } => "reticulum_preview",
+ Self::Hybrid { .. } => "hybrid",
+ Self::Proxy { .. } => "proxy",
+ }
+ }
+
+ pub(crate) fn target_set(&self) -> Result<Option<TargetSet>, RadrootsSdkError> {
+ match self {
+ Self::LocalOnly => Ok(None),
+ Self::Nostr { profile } => Ok(Some(profile.target_set().clone())),
+ Self::ReticulumPreview { profile } => Ok(Some(profile.target_set()?)),
+ Self::Hybrid { profile } => {
+ let mut targets = profile.nostr().target_set().targets().to_vec();
+ targets.extend(profile.reticulum_preview().target_set()?.into_targets());
+ Ok(Some(TargetSet::transport_targets(targets)?))
+ }
+ Self::Proxy { profile } => Ok(Some(profile.target_set()?)),
+ }
+ }
+
+ pub(crate) fn configured_nostr_relay_urls(&self) -> Vec<String> {
+ match self {
+ Self::Nostr { profile } => profile.relay_urls(),
+ Self::Hybrid { profile } => profile.nostr().relay_urls(),
+ Self::LocalOnly | Self::ReticulumPreview { .. } | Self::Proxy { .. } => Vec::new(),
+ }
+ }
+}
+
+impl From<RadrootsTransportDeliveryReceipt> for TransportReceipt {
+ fn from(value: RadrootsTransportDeliveryReceipt) -> Self {
+ Self {
+ request_id: value.request_id,
+ target_receipts: value.target_receipts,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct TransportReceipt {
+ pub request_id: String,
+ pub target_receipts: Vec<RadrootsTransportTargetReceipt>,
+}
+
+impl TransportReceipt {
+ pub fn satisfied_target_count(&self) -> usize {
+ self.target_receipts
+ .iter()
+ .filter(|receipt| receipt.status.counts_as_satisfied())
+ .count()
+ }
+}
+
+fn normalized_nostr_relay_url(
+ value: &str,
+ policy: NostrRelayUrlPolicy,
+) -> Result<String, RadrootsSdkError> {
+ let relay = RadrootsRelayUrl::parse(value, policy.nostr_transport_policy())?;
+ Ok(relay.into_string())
+}
+
+#[cfg(test)]
+#[path = "../tests/unit/transport_tests.rs"]
+mod tests;
diff --git a/crates/sdk/src/workflow_runtime.rs b/crates/sdk/src/workflow_runtime.rs
@@ -1,8 +1,8 @@
#[cfg(feature = "signer-adapters")]
use crate::RadrootsSdkSignRequest;
use crate::{
- RadrootsClient, RadrootsSdkError, SdkIdempotencyKey, SdkRelayTargetPolicy, SdkRelayTargetSet,
- runtime::sdk_now_ms,
+ RadrootsClient, RadrootsSdkError, SatisfactionPolicy, SdkIdempotencyKey, TargetPolicy,
+ TargetSet, runtime::sdk_now_ms,
};
use radroots_authority::{RadrootsActorContext, RadrootsEventSigner, sign_authorized_draft};
use radroots_event_store::RadrootsEventIngest;
@@ -11,7 +11,11 @@ use radroots_events::{
draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent},
ids::RadrootsEventId,
};
-use radroots_outbox::{RadrootsOutboxEnqueueStatus, RadrootsOutboxSignedOperationInput};
+use radroots_outbox::{
+ RadrootsOutboxDeliveryPlanInput, RadrootsOutboxEnqueueStatus,
+ RadrootsOutboxSignedOperationInput,
+};
+use radroots_transport::RadrootsTransportSatisfactionPolicy;
#[cfg(test)]
use sha2::{Digest, Sha256};
@@ -19,7 +23,8 @@ pub(crate) struct SdkWorkflowEnqueueRequest<'a> {
pub(crate) operation_kind: &'static str,
pub(crate) actor: &'a RadrootsActorContext,
pub(crate) frozen_draft: &'a RadrootsFrozenEventDraft,
- pub(crate) target_relays: SdkRelayTargetPolicy,
+ pub(crate) target_relays: TargetPolicy,
+ pub(crate) satisfaction_policy: SatisfactionPolicy,
pub(crate) idempotency_key: Option<SdkIdempotencyKey>,
}
@@ -37,9 +42,10 @@ pub(crate) async fn enqueue_signed_workflow(
request: SdkWorkflowEnqueueRequest<'_>,
signer: &dyn RadrootsEventSigner,
) -> Result<SdkWorkflowEnqueueReceipt, RadrootsSdkError> {
- let target_relays = resolved_target_relays(sdk, &request.target_relays)?;
+ let delivery_plan =
+ resolved_delivery_plan(sdk, &request.target_relays, request.satisfaction_policy)?;
let signed_event = sign_authorized_draft(request.actor, signer, request.frozen_draft)?;
- enqueue_signed_workflow_event(sdk, request, signed_event, target_relays).await
+ enqueue_signed_workflow_event(sdk, request, signed_event, delivery_plan).await
}
#[cfg(feature = "signer-adapters")]
@@ -47,7 +53,8 @@ pub(crate) async fn enqueue_configured_signed_workflow(
sdk: &RadrootsClient,
request: SdkWorkflowEnqueueRequest<'_>,
) -> Result<SdkWorkflowEnqueueReceipt, RadrootsSdkError> {
- let target_relays = resolved_target_relays(sdk, &request.target_relays)?;
+ let delivery_plan =
+ resolved_delivery_plan(sdk, &request.target_relays, request.satisfaction_policy)?;
let signed_event = sdk
.sign_with_configured_signer(RadrootsSdkSignRequest::new(
request.operation_kind,
@@ -56,14 +63,14 @@ pub(crate) async fn enqueue_configured_signed_workflow(
))
.await?
.signed_event;
- enqueue_signed_workflow_event(sdk, request, signed_event, target_relays).await
+ enqueue_signed_workflow_event(sdk, request, signed_event, delivery_plan).await
}
async fn enqueue_signed_workflow_event(
sdk: &RadrootsClient,
request: SdkWorkflowEnqueueRequest<'_>,
signed_event: RadrootsSignedNostrEvent,
- target_relays: SdkResolvedRelayTargets,
+ delivery_plan: SdkResolvedDeliveryPlan,
) -> Result<SdkWorkflowEnqueueReceipt, RadrootsSdkError> {
let idempotency_key = match request.idempotency_key {
Some(idempotency_key) => idempotency_key,
@@ -71,22 +78,20 @@ async fn enqueue_signed_workflow_event(
request.operation_kind,
request.frozen_draft.expected_event_id.as_str(),
request.frozen_draft.expected_pubkey.as_str(),
- target_relays.canonical_relays.as_slice(),
+ delivery_plan.canonical_targets.as_slice(),
),
};
let observed_at_ms = sdk_now_ms(sdk)?;
let signed_event_id = RadrootsEventId::parse(request.frozen_draft.expected_event_id.as_str())
.expect("frozen workflow draft has a valid expected event id");
- let allow_empty_target_relays = target_relays.allow_empty_target_relays;
- let target_relay_values = target_relays.relays;
+ let delivery_plan_value = delivery_plan.delivery_plan;
let idempotency_key_for_enqueue = idempotency_key.clone();
let preflight_input = signed_outbox_input(
request.operation_kind,
request.frozen_draft,
signed_event.clone(),
- target_relay_values.clone(),
+ delivery_plan_value.clone(),
idempotency_key,
- allow_empty_target_relays,
false,
observed_at_ms,
);
@@ -94,7 +99,8 @@ async fn enqueue_signed_workflow_event(
._outbox
.preflight_signed_operation_idempotency(&preflight_input)
.await?;
- let partial_failure_digest_prefix = digest_prefix(preflight.idempotency_digest.as_str());
+ let partial_failure_digest_prefix =
+ digest_prefix(preflight.operation_idempotency_digest.as_str());
let event = event_from_signed(&signed_event);
let ingest = RadrootsEventIngest::new(event, observed_at_ms)
.with_raw_json(signed_event.raw_json.clone());
@@ -103,9 +109,8 @@ async fn enqueue_signed_workflow_event(
request.operation_kind,
request.frozen_draft,
signed_event,
- target_relay_values,
+ delivery_plan_value,
idempotency_key_for_enqueue,
- allow_empty_target_relays,
ingest_receipt.inserted,
observed_at_ms,
);
@@ -131,7 +136,8 @@ async fn enqueue_signed_workflow_event(
)
}
})?;
- let idempotency_digest_prefix = digest_prefix(outbox_receipt.idempotency_digest.as_str());
+ let idempotency_digest_prefix =
+ digest_prefix(outbox_receipt.operation_idempotency_digest.as_str());
Ok(SdkWorkflowEnqueueReceipt {
signed_event_id,
local_event_seq: ingest_receipt.seq,
@@ -142,46 +148,66 @@ async fn enqueue_signed_workflow_event(
})
}
-struct SdkResolvedRelayTargets {
- relays: Vec<String>,
- canonical_relays: Vec<String>,
- allow_empty_target_relays: bool,
+struct SdkResolvedDeliveryPlan {
+ delivery_plan: RadrootsOutboxDeliveryPlanInput,
+ canonical_targets: Vec<String>,
}
-fn resolved_target_relays(
+fn resolved_delivery_plan(
sdk: &RadrootsClient,
- target_relays: &SdkRelayTargetPolicy,
-) -> Result<SdkResolvedRelayTargets, RadrootsSdkError> {
+ target_relays: &TargetPolicy,
+ satisfaction_policy: SatisfactionPolicy,
+) -> Result<SdkResolvedDeliveryPlan, RadrootsSdkError> {
match target_relays {
- SdkRelayTargetPolicy::Explicit(target_relays) => Ok(SdkResolvedRelayTargets {
- relays: target_relays.relays().to_vec(),
- canonical_relays: target_relays.canonical_relays().to_vec(),
- allow_empty_target_relays: false,
- }),
- SdkRelayTargetPolicy::UseConfiguredRelays => {
+ TargetPolicy::Explicit(target_relays) => {
+ delivery_plan_from_target_set("explicit", target_relays.clone(), satisfaction_policy)
+ }
+ TargetPolicy::UseConfiguredProfile => {
let target_relays =
- SdkRelayTargetSet::from_normalized_relays(sdk.relay_urls().to_vec())?;
- Ok(SdkResolvedRelayTargets {
- relays: target_relays.relays().to_vec(),
- canonical_relays: target_relays.canonical_relays().to_vec(),
- allow_empty_target_relays: false,
- })
+ TargetSet::from_normalized_nostr_relays(sdk.configured_nostr_relay_urls())?;
+ delivery_plan_from_target_set("configured_profile", target_relays, satisfaction_policy)
}
- SdkRelayTargetPolicy::UsePublishTransport => {
- if sdk
- .publish_transport()
- .supports_delegated_relay_resolution()
- {
- Ok(SdkResolvedRelayTargets {
- relays: Vec::new(),
- canonical_relays: Vec::new(),
- allow_empty_target_relays: true,
- })
- } else {
- Err(RadrootsSdkError::empty_target_relays(
- "publish transport relay resolution",
- ))
- }
+ TargetPolicy::UseTransportProfile => {
+ let target_set = sdk.transport_profile().target_set()?.ok_or_else(|| {
+ RadrootsSdkError::empty_target_relays("publish transport profile")
+ })?;
+ delivery_plan_from_target_set(
+ sdk.transport_profile().transport_profile_id(),
+ target_set,
+ satisfaction_policy,
+ )
+ }
+ }
+}
+
+fn delivery_plan_from_target_set(
+ transport_profile_id: impl Into<String>,
+ target_set: TargetSet,
+ satisfaction_policy: SatisfactionPolicy,
+) -> Result<SdkResolvedDeliveryPlan, RadrootsSdkError> {
+ let canonical_targets = target_set.canonical_targets().to_vec();
+ let delivery_plan = RadrootsOutboxDeliveryPlanInput::new(
+ transport_profile_id,
+ 1,
+ transport_satisfaction_policy(satisfaction_policy),
+ target_set.into_targets(),
+ );
+ Ok(SdkResolvedDeliveryPlan {
+ delivery_plan,
+ canonical_targets,
+ })
+}
+
+fn transport_satisfaction_policy(
+ satisfaction_policy: SatisfactionPolicy,
+) -> RadrootsTransportSatisfactionPolicy {
+ match satisfaction_policy {
+ SatisfactionPolicy::NoWait | SatisfactionPolicy::AllTargets => {
+ RadrootsTransportSatisfactionPolicy::AllTargets
+ }
+ SatisfactionPolicy::AtLeastOneTarget => RadrootsTransportSatisfactionPolicy::AnyTarget,
+ SatisfactionPolicy::AtLeast { required } => {
+ RadrootsTransportSatisfactionPolicy::AtLeast(required)
}
}
}
@@ -226,27 +252,21 @@ fn signed_outbox_input(
operation_kind: &'static str,
frozen_draft: &RadrootsFrozenEventDraft,
signed_event: RadrootsSignedNostrEvent,
- target_relays: Vec<String>,
+ delivery_plan: RadrootsOutboxDeliveryPlanInput,
idempotency_key: SdkIdempotencyKey,
- allow_empty_target_relays: bool,
event_store_inserted: bool,
observed_at_ms: i64,
) -> RadrootsOutboxSignedOperationInput {
- let input = RadrootsOutboxSignedOperationInput::new(
+ RadrootsOutboxSignedOperationInput::new(
operation_kind,
frozen_draft.clone(),
signed_event,
- target_relays,
+ delivery_plan,
event_store_inserted,
observed_at_ms,
observed_at_ms,
)
- .with_idempotency_key(idempotency_key.into_string());
- if allow_empty_target_relays {
- input.allow_empty_target_relays()
- } else {
- input
- }
+ .with_idempotency_key(idempotency_key.into_string())
}
fn event_from_signed(signed_event: &RadrootsSignedNostrEvent) -> RadrootsNostrEvent {
diff --git a/crates/sdk/tests/dvm_runtime.rs b/crates/sdk/tests/dvm_runtime.rs
@@ -29,11 +29,10 @@ use radroots_nostr::prelude::{
use radroots_outbox::RadrootsOutbox;
use radroots_sdk::{
DVM_TRADE_TRANSITION_PROOF_REQUEST_OPERATION_KIND, DvmTradeTransitionProofEnqueueRequest,
- DvmTradeTransitionProofPrepareRequest, DvmValidationReceiptIngestRequest, RadrootsClient,
- RadrootsSdkError, RadrootsSdkStorageConfig, RadrootsSdkTimestamp,
+ DvmTradeTransitionProofPrepareRequest, DvmValidationReceiptIngestRequest, NostrRelayUrlPolicy,
+ RadrootsClient, RadrootsSdkError, RadrootsSdkStorageConfig, RadrootsSdkTimestamp,
RadrootsTradeInventoryBinWitnessDto, RadrootsTradeValidationTrustState, SdkMutationState,
- SdkRelayTargetPolicy, SdkRelayUrlPolicy, TradeStatusKind, TradeStatusNextActionKind,
- TradeStatusRequest,
+ TargetPolicy, TradeStatusKind, TradeStatusNextActionKind, TradeStatusRequest,
};
#[cfg(feature = "signer-adapters")]
use radroots_sdk::{RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider};
@@ -235,7 +234,7 @@ async fn dvm_configured_enqueue_reports_prepare_and_target_errors_without_mutati
deterministic_event_id("request-event"),
deterministic_event_id("decision-event"),
inventory_bins(),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
);
let error = sdk
.dvm()
@@ -412,7 +411,7 @@ async fn dvm_trade_transition_proof_request_reports_prepare_and_target_errors_wi
deterministic_event_id("request-event"),
deterministic_event_id("decision-event"),
inventory_bins(),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
);
let error = sdk
.dvm()
@@ -664,8 +663,8 @@ fn service_actor(signer: &FixtureSigner) -> RadrootsActorContext {
.expect("service actor")
}
-fn explicit_relays() -> SdkRelayTargetPolicy {
- SdkRelayTargetPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public).expect("relay targets")
+fn explicit_relays() -> TargetPolicy {
+ TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public).expect("relay targets")
}
fn inventory_bins() -> Vec<RadrootsTradeInventoryBinWitnessDto> {
diff --git a/crates/sdk/tests/farms_runtime.rs b/crates/sdk/tests/farms_runtime.rs
@@ -12,18 +12,18 @@ use radroots_events::{
kinds::{KIND_FARM, KIND_PROFILE},
};
use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState};
-use radroots_relay_transport::RadrootsMockRelayPublishAdapter;
use radroots_sdk::{
FARM_PUBLISH_OPERATION_KIND, FarmEnqueuePublishRequest, FarmPreparePublishRequest,
FarmPrivateLocationClearRequest, FarmPrivateLocationInput, FarmPrivateLocationLookupCandidate,
FarmPrivateLocationLookupReceipt, FarmPrivateLocationReceipt, FarmPrivateLocationSetRequest,
FarmPrivateLocationSetResult, FarmPrivateLocationUpsertRequest, Geocoder,
- GeocoderLocalityQuery, PushOutboxEventState, PushOutboxRelayOutcomeKind, PushOutboxRequest,
- RadrootsClient, RadrootsSdkError, RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind,
- RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, SdkExactLocation, SdkIdempotencyKey,
- SdkMutationState, SdkPublicLocality, SdkRelayTargetPolicy, SdkRelayTargetSet,
- SdkRelayUrlPolicy, StorageStatusRequest,
+ GeocoderLocalityQuery, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState,
+ PushOutboxRelayOutcomeKind, PushOutboxRequest, RadrootsClient, RadrootsSdkError,
+ RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind, RadrootsSdkRecoveryAction,
+ RadrootsSdkTimestamp, SdkExactLocation, SdkIdempotencyKey, SdkMutationState, SdkPublicLocality,
+ StorageStatusRequest, TargetPolicy, TargetSet, TransportProfile,
};
+use radroots_transport_nostr::RadrootsMockRelayPublishAdapter;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
#[path = "support/serializer_failure.rs"]
@@ -138,8 +138,11 @@ async fn directory_sdk_with_relays(relays: &[&str]) -> (tempfile::TempDir, Radro
let mut builder = RadrootsClient::builder()
.directory_storage(tempdir.path().join("sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000));
- for relay in relays {
- builder = builder.relay_url(*relay);
+ if !relays.is_empty() {
+ builder = builder.transport_profile(TransportProfile::nostr(
+ NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ ));
}
let sdk = builder.build().await.expect("sdk");
(tempdir, sdk)
@@ -606,7 +609,7 @@ async fn farm_enqueue_publish_stores_event_and_queues_signed_outbox_without_prof
let request = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_B_D_TAG, "North Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("farm-idem-b")
.expect("idempotency key");
@@ -672,7 +675,7 @@ async fn farm_enqueue_publish_returns_sanitized_signer_errors_before_mutation()
let request = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_C_D_TAG, "North Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
);
let error = sdk
.farms()
@@ -718,15 +721,15 @@ async fn farm_enqueue_publish_derives_order_independent_idempotency_key() {
let first = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_D_D_TAG, "North Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY_B, RELAY, RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY_B, RELAY, RELAY], NostrRelayUrlPolicy::Public)
.expect("first target relays");
let second = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_D_D_TAG, "North Farm"),
- SdkRelayTargetPolicy::explicit(
- SdkRelayTargetSet::new([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ TargetPolicy::explicit(
+ TargetSet::new([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("second target relays"),
),
);
@@ -757,11 +760,11 @@ async fn farm_enqueue_publish_derives_order_independent_idempotency_key() {
.await
.expect("outbox");
let relay_urls = outbox
- .relay_statuses(first_receipt.outbox_event_id)
+ .delivery_targets(first_receipt.outbox_event_id)
.await
- .expect("relay statuses")
+ .expect("delivery targets")
.into_iter()
- .map(|status| status.relay_url)
+ .map(|target| target.endpoint_uri.to_string())
.collect::<Vec<_>>();
assert_eq!(relay_urls, vec![RELAY_B.to_owned(), RELAY.to_owned()]);
}
@@ -772,9 +775,9 @@ async fn farm_enqueue_publish_pushes_queued_event_with_mock_relay_sync() {
let enqueue_request = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_D_D_TAG, "Sync Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let enqueue_receipt = sdk
.farms()
@@ -829,7 +832,7 @@ async fn farm_enqueue_publish_reports_preflight_idempotency_conflict_without_mut
let first = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_E_D_TAG, "North Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("farm-idem-e")
.expect("idempotency key");
@@ -864,7 +867,7 @@ async fn farm_enqueue_publish_reports_preflight_idempotency_conflict_without_mut
let second = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_F_D_TAG, "Changed Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("farm-idem-e")
.expect("idempotency key");
@@ -938,9 +941,9 @@ async fn farm_runtime_dtos_serialize_deterministically() {
let enqueue_request = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_B_D_TAG, "Queued Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("relay targets")
.with_idempotency_key(
SdkIdempotencyKey::new("farm-serialized-idempotency").expect("idempotency"),
@@ -970,8 +973,22 @@ async fn farm_runtime_dtos_serialize_deterministically() {
},
"target_relays": {
"kind": "explicit",
- "relays": [RELAY, RELAY_B],
- "canonical_relays": [RELAY_B, RELAY]
+ "targets": [
+ {
+ "kind": "Nostr",
+ "uri": RELAY,
+ "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ },
+ {
+ "kind": "Nostr",
+ "uri": RELAY_B,
+ "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05"
+ }
+ ],
+ "canonical_targets": [
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05",
+ "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ ]
},
"idempotency_key": { "value": "<redacted>", "len": 27 },
"created_at": 1_700_000_123
@@ -986,7 +1003,7 @@ async fn farm_runtime_dtos_serialize_deterministically() {
let try_key_request = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_C_D_TAG, "Queued Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("farm-serialized-try-key")
.expect("try idempotency key");
diff --git a/crates/sdk/tests/listings_runtime.rs b/crates/sdk/tests/listings_runtime.rs
@@ -19,9 +19,9 @@ use radroots_events::{
use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState};
use radroots_sdk::{
LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest, ListingPreparePublishRequest,
- RadrootsClient, RadrootsSdkError, RadrootsSdkRecoveryAction, RadrootsSdkTimestamp,
- SdkIdempotencyKey, SdkMutationState, SdkRelayTargetPolicy, SdkRelayTargetSet,
- SdkRelayUrlPolicy,
+ NostrProfile, NostrRelayUrlPolicy, RadrootsClient, RadrootsSdkError, RadrootsSdkRecoveryAction,
+ RadrootsSdkTimestamp, SdkIdempotencyKey, SdkMutationState, TargetPolicy, TargetSet,
+ TransportProfile,
};
use radroots_trade::listing::RadrootsListingDraftDocumentV1;
@@ -171,8 +171,11 @@ async fn directory_sdk_with_relays(relays: &[&str]) -> (tempfile::TempDir, Radro
let mut builder = RadrootsClient::builder()
.directory_storage(tempdir.path().join("sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000));
- for relay in relays {
- builder = builder.relay_url(*relay);
+ if !relays.is_empty() {
+ builder = builder.transport_profile(TransportProfile::nostr(
+ NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ ));
}
let sdk = builder.build().await.expect("sdk");
(tempdir, sdk)
@@ -238,7 +241,7 @@ async fn enqueue_publish_stores_event_and_queues_signed_outbox_without_publish()
let request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_B_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("idem-b")
.expect("idempotency key");
@@ -290,24 +293,24 @@ async fn enqueue_publish_stores_event_and_queues_signed_outbox_without_publish()
}
#[tokio::test]
-async fn enqueue_publish_use_configured_relays_rejects_empty_builder_relays() {
+async fn enqueue_publish_use_configured_profile_rejects_empty_transport_targets() {
let (_tempdir, sdk) = directory_sdk_with_relays(&[]).await;
let request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
);
let error = sdk
.listings()
.enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(SELLER))
.await
- .expect_err("empty configured relays");
+ .expect_err("empty configured profile");
assert!(matches!(
error,
RadrootsSdkError::EmptyTargetRelays { operation }
- if operation == "sdk relay target set"
+ if operation == "sdk transport target set"
));
}
@@ -327,7 +330,7 @@ async fn prepare_then_enqueue_prepared_uses_same_event_id() {
.enqueue_prepared_publish_with_explicit_signer(
&actor,
prepared.clone(),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
None,
&FixtureSigner::new(SELLER),
)
@@ -366,7 +369,7 @@ async fn enqueue_receipt_debug_omits_signed_event_payload_material() {
let request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("debug-secret-idempotency")
.expect("idempotency key");
@@ -425,9 +428,9 @@ async fn listing_runtime_dtos_serialize_deterministically() {
let enqueue_request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_B_D_TAG, "Queued Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("relay targets")
.with_idempotency_key(SdkIdempotencyKey::new("serialized-idempotency").expect("idempotency"))
.with_created_at(created_at);
@@ -436,12 +439,26 @@ async fn listing_runtime_dtos_serialize_deterministically() {
assert_eq!(enqueue_json["target_relays"]["kind"], "explicit");
assert_eq!(
- enqueue_json["target_relays"]["relays"],
- serde_json::json!([RELAY, RELAY_B])
+ enqueue_json["target_relays"]["targets"],
+ serde_json::json!([
+ {
+ "kind": "Nostr",
+ "uri": RELAY,
+ "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ },
+ {
+ "kind": "Nostr",
+ "uri": RELAY_B,
+ "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05"
+ }
+ ])
);
assert_eq!(
- enqueue_json["target_relays"]["canonical_relays"],
- serde_json::json!([RELAY_B, RELAY])
+ enqueue_json["target_relays"]["canonical_targets"],
+ serde_json::json!([
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05",
+ "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ ])
);
assert_eq!(
enqueue_json["idempotency_key"],
@@ -452,7 +469,7 @@ async fn listing_runtime_dtos_serialize_deterministically() {
let try_key_request = ListingEnqueuePublishRequest::from_document(
actor(),
RadrootsListingDraftDocumentV1::new(listing(LISTING_C_D_TAG, "Queued Coffee")),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("listing-serialized-try-key")
.expect("try idempotency key");
@@ -489,7 +506,7 @@ async fn enqueue_publish_convenience_matches_prepare_plus_enqueue_prepared() {
.enqueue_prepared_publish_with_explicit_signer(
&prepared_actor,
prepared_plan,
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
None,
&FixtureSigner::new(SELLER),
)
@@ -500,7 +517,7 @@ async fn enqueue_publish_convenience_matches_prepare_plus_enqueue_prepared() {
let convenience_request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_H_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
);
let convenience_receipt = convenience_sdk
.listings()
@@ -526,7 +543,7 @@ async fn enqueue_prepared_publish_returns_structured_actor_errors() {
.enqueue_prepared_publish_with_explicit_signer(
&non_seller_actor(),
prepared,
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
None,
&FixtureSigner::new(SELLER),
)
@@ -552,7 +569,7 @@ async fn enqueue_prepared_publish_returns_sanitized_signer_errors() {
.enqueue_prepared_publish_with_explicit_signer(
&actor,
prepared,
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
None,
&FixtureSigner::new(OTHER),
)
@@ -576,7 +593,7 @@ async fn explicit_historical_created_at_does_not_backdate_observed_at_ms() {
let request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_K_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.with_created_at(created_at);
@@ -622,7 +639,7 @@ async fn enqueue_publish_returns_sanitized_signer_errors() {
let request = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_C_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
);
let error = sdk
.listings()
@@ -645,7 +662,7 @@ async fn enqueue_publish_reports_preflight_idempotency_conflict_without_mutation
let first = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_D_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("idem-d")
.expect("idempotency key");
@@ -680,7 +697,7 @@ async fn enqueue_publish_reports_preflight_idempotency_conflict_without_mutation
let second = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_E_D_TAG, "Changed"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("idem-d")
.expect("idempotency key");
@@ -724,15 +741,15 @@ async fn enqueue_publish_derives_order_independent_idempotency_key() {
let first = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_F_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY_B, RELAY, RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY_B, RELAY, RELAY], NostrRelayUrlPolicy::Public)
.expect("first target relays");
let second = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_F_D_TAG, "Coffee"),
- SdkRelayTargetPolicy::explicit(
- SdkRelayTargetSet::new([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ TargetPolicy::explicit(
+ TargetSet::new([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("second target relays"),
),
);
@@ -763,11 +780,11 @@ async fn enqueue_publish_derives_order_independent_idempotency_key() {
.await
.expect("outbox");
let relay_urls = outbox
- .relay_statuses(first_receipt.outbox_event_id)
+ .delivery_targets(first_receipt.outbox_event_id)
.await
- .expect("relay statuses")
+ .expect("delivery targets")
.into_iter()
- .map(|status| status.relay_url)
+ .map(|target| target.endpoint_uri.to_string())
.collect::<Vec<_>>();
assert_eq!(relay_urls, vec![RELAY_B.to_owned(), RELAY.to_owned()]);
}
diff --git a/crates/sdk/tests/market_runtime.rs b/crates/sdk/tests/market_runtime.rs
@@ -22,8 +22,9 @@ use radroots_nostr::prelude::{
RadrootsNostrKeys, RadrootsNostrSecretKey, radroots_nostr_sign_frozen_draft,
};
use radroots_sdk::{
- ListingEnqueuePublishRequest, MarketSearchRequest, RadrootsClient, RadrootsSdkError,
- RadrootsSdkTimestamp, SdkRelayTargetPolicy, SdkRelayUrlPolicy, SyncProjectionRefreshRequest,
+ ListingEnqueuePublishRequest, MarketSearchRequest, NostrProfile, NostrRelayUrlPolicy,
+ RadrootsClient, RadrootsSdkError, RadrootsSdkTimestamp, SyncProjectionRefreshRequest,
+ TargetPolicy, TransportProfile,
};
const SELLER_SECRET_KEY_HEX: &str =
@@ -134,7 +135,9 @@ async fn directory_sdk() -> (tempfile::TempDir, RadrootsClient) {
let sdk = RadrootsClient::builder()
.directory_storage(tempdir.path().join("sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
- .relay_url(RELAY)
+ .transport_profile(TransportProfile::nostr(
+ NostrProfile::new([RELAY], NostrRelayUrlPolicy::Public).expect("Nostr profile"),
+ ))
.build()
.await
.expect("sdk");
@@ -147,9 +150,9 @@ async fn market_search_refreshes_local_projection_and_reads_fts() {
let publish = ListingEnqueuePublishRequest::new(
seller_actor(),
listing("Blueberries"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let receipt = sdk
.listings()
diff --git a/crates/sdk/tests/orders_runtime.rs b/crates/sdk/tests/orders_runtime.rs
@@ -39,29 +39,24 @@ use radroots_nostr::prelude::{
};
use radroots_outbox::RadrootsOutbox;
#[cfg(feature = "relay-runtime")]
-use radroots_relay_transport::{
- RadrootsMockRelayFetchAdapter, RadrootsRelayFetchAdapter, RadrootsRelayFetchItem,
- RadrootsRelayFetchRequest, RadrootsRelayTransportError,
-};
-use radroots_sdk::{
- AckPolicy, PublishMode, RadrootsClient, RadrootsSdkError, RadrootsSdkRecoveryAction,
- RadrootsSdkTimestamp, RadrootsTradeValidationTrustPolicy, RadrootsTradeValidationTrustState,
- RelayResolutionPolicy, SdkMutationState, SdkRelayTargetSet, SdkRelayUrlPolicy,
- SdkTradeStatusIssue, SdkTradeStatusIssueKind, SdkTradeStatusSource, TRADE_STATUS_DEFAULT_LIMIT,
- TRADE_STATUS_MAX_LIMIT, TRADE_STATUS_WATCH_MAX_CAPACITY, TRADE_SUBMIT_OPERATION_KIND,
- TradeAcceptRequest, TradeCancelRequest, TradeDeclineRequest, TradeEvidenceIngestRequest,
- TradeEvidenceMode, TradeMutationOutcome, TradeProposeRequest,
- TradeRequestEvidenceIngestRequest, TradeRevisionDecisionRequest, TradeRevisionProposalRequest,
- TradeStatusKind, TradeStatusNextActionKind, TradeStatusRequest, TradeStatusWatchCancelState,
- TradeStatusWatchRequest,
-};
-#[cfg(feature = "relay-runtime")]
use radroots_sdk::{
DvmValidationReceiptIngestRequest, TradeEvidenceQueryBranchKind, TradeResyncRelayOutcomeKind,
TradeResyncRelayTransportOutcomeKind, TradeResyncRequest, TradeSellerInboxRequest,
TradeValidationReceiptInspectRequest, TradeValidationReceiptListRequest,
TradeValidationReceiptVerifyRequest,
};
+use radroots_sdk::{
+ NostrProfile, NostrRelayUrlPolicy, PublishMode, RadrootsClient, RadrootsSdkError,
+ RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, RadrootsTradeValidationTrustPolicy,
+ RadrootsTradeValidationTrustState, SatisfactionPolicy, SdkMutationState, SdkTradeStatusIssue,
+ SdkTradeStatusIssueKind, SdkTradeStatusSource, TRADE_STATUS_DEFAULT_LIMIT,
+ TRADE_STATUS_MAX_LIMIT, TRADE_STATUS_WATCH_MAX_CAPACITY, TRADE_SUBMIT_OPERATION_KIND,
+ TargetPolicy, TargetSet, TradeAcceptRequest, TradeCancelRequest, TradeDeclineRequest,
+ TradeEvidenceIngestRequest, TradeEvidenceMode, TradeMutationOutcome, TradeProposeRequest,
+ TradeRequestEvidenceIngestRequest, TradeRevisionDecisionRequest, TradeRevisionProposalRequest,
+ TradeStatusKind, TradeStatusNextActionKind, TradeStatusRequest, TradeStatusWatchCancelState,
+ TradeStatusWatchRequest, TransportProfile,
+};
use radroots_sdk::{PrivacyPreflightConfirmation, PrivacyPreflightStatus, ProductSensitivityField};
#[cfg(all(feature = "signer-adapters", feature = "local-signer"))]
use radroots_sdk::{RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider};
@@ -74,6 +69,11 @@ use radroots_trade::validation_receipt::{
RadrootsValidationReceiptStatement, RadrootsValidationReceiptType,
validation_receipt_event_build, validation_receipt_public_values_hash_hex,
};
+#[cfg(feature = "relay-runtime")]
+use radroots_transport_nostr::{
+ RadrootsMockRelayFetchAdapter, RadrootsRelayFetchAdapter, RadrootsRelayFetchItem,
+ RadrootsRelayFetchRequest, RadrootsRelayTransportError,
+};
use serde::Serialize;
use serde::ser::{self, SerializeStruct};
@@ -470,8 +470,11 @@ async fn directory_sdk_and_store_with_relays(
let mut builder = RadrootsClient::builder()
.directory_storage(tempdir.path().join("sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000));
- for relay in relays {
- builder = builder.relay_url(*relay);
+ if !relays.is_empty() {
+ builder = builder.transport_profile(TransportProfile::nostr(
+ NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ ));
}
let sdk = builder.build().await.expect("sdk");
let store =
@@ -500,8 +503,11 @@ async fn directory_sdk_with_signer_and_relays(
.signer_provider(RadrootsSdkSignerProvider::LocalKey(
RadrootsSdkLocalKeySigner::new(signer_keys).expect("local signer"),
));
- for relay in relays {
- builder = builder.relay_url(*relay);
+ if !relays.is_empty() {
+ builder = builder.transport_profile(TransportProfile::nostr(
+ NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ ));
}
builder.build().await.expect("sdk")
}
@@ -556,9 +562,9 @@ fn listing_event_ptr() -> RadrootsNostrEventPtr {
}
}
-fn explicit_trade_relays() -> RelayResolutionPolicy {
- RelayResolutionPolicy::explicit(
- SdkRelayTargetSet::new([RELAY], SdkRelayUrlPolicy::Public).expect("target relays"),
+fn explicit_trade_relays() -> TargetPolicy {
+ TargetPolicy::explicit(
+ TargetSet::new([RELAY], NostrRelayUrlPolicy::Public).expect("target relays"),
)
}
@@ -641,7 +647,7 @@ fn order_request(raw_order_id: &str) -> RadrootsOrderRequest {
fn trade_propose_request(
raw_order_id: &str,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> TradeProposeRequest {
let order = order_request(raw_order_id);
TradeProposeRequest::new(
@@ -843,11 +849,11 @@ async fn order_submit_enqueue_stores_event_queues_outbox_and_status_sees_request
buyer_actor(),
listing_event_ptr(),
order,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays")
.try_with_idempotency_key("order-submit-enqueue-idempotency")
.expect("idempotency key");
@@ -978,7 +984,7 @@ async fn trade_product_clients_propose_inbox_accept_status_and_resync() {
trade_propose_request(
"trade-product-facade-flow",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-facade-propose")
.expect("propose idempotency"),
@@ -1049,7 +1055,7 @@ async fn trade_product_clients_propose_inbox_accept_status_and_resync() {
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.try_with_idempotency_key("trade-product-facade-accept")
@@ -1136,7 +1142,7 @@ async fn trade_product_clients_resync_committed_after_rhi_validation_receipt() {
trade_propose_request(
"trade-product-committed-resync",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-committed-resync-propose")
.expect("propose idempotency"),
@@ -1185,7 +1191,7 @@ async fn trade_product_clients_resync_committed_after_rhi_validation_receipt() {
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.try_with_idempotency_key("trade-product-committed-resync-accept")
@@ -1444,7 +1450,7 @@ async fn trade_product_accept_resync_before_mutation_imports_relay_visible_reque
trade_propose_request(
"trade-product-resync-before-accept",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-resync-before-accept-propose")
.expect("propose idempotency"),
@@ -1479,7 +1485,7 @@ async fn trade_product_accept_resync_before_mutation_imports_relay_visible_reque
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::ResyncBeforeMutation,
)
.try_with_idempotency_key("trade-product-resync-before-accept")
@@ -1566,7 +1572,7 @@ async fn trade_product_revision_status_resync_imports_pending_revision_proposal(
trade_propose_request(
"trade-product-resync-before-revision-decision",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-resync-before-revision-decision-propose")
.expect("propose idempotency"),
@@ -1612,7 +1618,7 @@ async fn trade_product_revision_status_resync_imports_pending_revision_proposal(
"increase quantity",
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.with_privacy_confirmation(public_note_confirmation())
@@ -1694,7 +1700,7 @@ async fn trade_product_accept_local_only_does_not_fetch_relay_evidence() {
trade_propose_request(
"trade-product-local-only-no-fetch",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-local-only-no-fetch-propose")
.expect("propose idempotency"),
@@ -1716,7 +1722,7 @@ async fn trade_product_accept_local_only_does_not_fetch_relay_evidence() {
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
),
&adapter,
@@ -1763,7 +1769,7 @@ async fn trade_product_accept_require_explicit_evidence_ingests_supplied_request
trade_propose_request(
"trade-product-explicit-accept",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-explicit-accept-propose")
.expect("propose idempotency"),
@@ -1786,7 +1792,7 @@ async fn trade_product_accept_require_explicit_evidence_ingests_supplied_request
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::require_explicit_evidence([
TradeEvidenceIngestRequest::new(request_event),
]),
@@ -1839,7 +1845,7 @@ async fn trade_product_accept_require_explicit_evidence_rejects_empty_evidence()
trade_propose_request(
"trade-product-empty-explicit-accept",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-empty-explicit-accept-propose")
.expect("propose idempotency"),
@@ -1859,7 +1865,7 @@ async fn trade_product_accept_require_explicit_evidence_rejects_empty_evidence()
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::require_explicit_evidence(Vec::<TradeEvidenceIngestRequest>::new()),
))
.await
@@ -2707,7 +2713,7 @@ async fn trade_product_propose_idempotency_replays_same_payload_and_conflicts_di
let request = trade_propose_request(
"trade-product-idempotent",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-idempotent-key")
.expect("idempotency");
@@ -2762,7 +2768,7 @@ async fn trade_product_propose_idempotency_replays_same_payload_and_conflicts_di
trade_propose_request(
"trade-product-idempotent-conflict",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-idempotent-key")
.expect("conflict idempotency"),
@@ -2810,7 +2816,7 @@ async fn trade_product_propose_requires_public_note_privacy_confirmation() {
trade_propose_request(
"trade-product-propose-public-note",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_public_note("please leave at the community table"),
)
@@ -2866,7 +2872,7 @@ async fn trade_product_propose_publishes_public_note_after_confirmation() {
trade_propose_request(
"trade-product-propose-public-note-confirmed",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_public_note("please leave at the community table")
.with_privacy_confirmation(public_note_confirmation())
@@ -2909,7 +2915,7 @@ async fn trade_product_propose_blocks_sensitive_fulfillment_note_even_when_confi
trade_propose_request(
"trade-product-propose-sensitive-note",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_public_note("pickup address is 123 Farm Lane")
.with_privacy_confirmation(public_note_confirmation()),
@@ -2956,7 +2962,7 @@ async fn trade_product_decline_requires_public_reason_privacy_confirmation() {
trade_propose_request(
"trade-product-privacy-decline",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-privacy-decline-propose")
.expect("propose idempotency"),
@@ -2975,7 +2981,7 @@ async fn trade_product_decline_requires_public_reason_privacy_confirmation() {
"sold elsewhere",
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
))
.await
@@ -3024,7 +3030,7 @@ async fn trade_product_decline_requires_public_reason_privacy_confirmation() {
"sold elsewhere",
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.with_privacy_confirmation(public_note_confirmation())
@@ -3060,7 +3066,7 @@ async fn trade_product_cancel_blocks_sensitive_fulfillment_reason_before_mutatio
trade_propose_request(
"trade-product-privacy-cancel",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-privacy-cancel-propose")
.expect("propose idempotency"),
@@ -3079,7 +3085,7 @@ async fn trade_product_cancel_blocks_sensitive_fulfillment_reason_before_mutatio
"pickup address is 123 Farm Lane",
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.with_privacy_confirmation(public_note_confirmation()),
@@ -3126,7 +3132,7 @@ async fn trade_product_cancel_enqueues_with_locator_and_updates_status() {
trade_propose_request(
"trade-product-cancel",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-cancel-propose")
.expect("propose idempotency"),
@@ -3146,7 +3152,7 @@ async fn trade_product_cancel_enqueues_with_locator_and_updates_status() {
"changed plan",
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.with_privacy_confirmation(public_note_confirmation())
@@ -3191,7 +3197,7 @@ async fn trade_product_revision_lifecycle_uses_locator_and_updates_status() {
trade_propose_request(
"trade-product-revision",
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trade-product-revision-propose")
.expect("propose idempotency"),
@@ -3218,7 +3224,7 @@ async fn trade_product_revision_lifecycle_uses_locator_and_updates_status() {
"increase quantity",
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.with_privacy_confirmation(public_note_confirmation())
@@ -3252,7 +3258,7 @@ async fn trade_product_revision_lifecycle_uses_locator_and_updates_status() {
RadrootsOrderRevisionOutcome::Accepted,
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
)
.try_with_idempotency_key("trade-product-revision-decision")
@@ -3285,7 +3291,7 @@ async fn trade_product_propose_dry_run_returns_plan_without_local_side_effects()
.propose_trade(trade_propose_request(
"trade-product-dry-run",
PublishMode::DryRun,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await
.expect("dry-run proposal");
@@ -3326,11 +3332,11 @@ async fn order_submit_enqueue_returns_sanitized_signer_errors_before_mutation()
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-wrong-signer"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let error = sdk
@@ -3376,22 +3382,22 @@ async fn order_submit_enqueue_derives_order_independent_idempotency_key() {
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-idempotent"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY_B, RELAY, RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY_B, RELAY, RELAY], NostrRelayUrlPolicy::Public)
.expect("first target relays");
let second = TradeSubmitEnqueueRequest::new(
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-idempotent"),
- RelayResolutionPolicy::explicit(
- SdkRelayTargetSet::new([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ TargetPolicy::explicit(
+ TargetSet::new([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("second target relays"),
),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
);
let first_receipt = sdk
@@ -3446,11 +3452,11 @@ async fn order_submit_enqueue_derives_order_independent_idempotency_key() {
.await
.expect("outbox");
let relay_urls = outbox
- .relay_statuses(first_receipt.outbox_event_id)
+ .delivery_targets(first_receipt.outbox_event_id)
.await
- .expect("relay statuses")
+ .expect("delivery targets")
.into_iter()
- .map(|status| status.relay_url)
+ .map(|target| target.endpoint_uri.to_string())
.collect::<Vec<_>>();
assert_eq!(relay_urls, vec![RELAY_B.to_owned(), RELAY.to_owned()]);
}
@@ -3463,11 +3469,11 @@ async fn order_submit_enqueue_pushes_queued_event_with_mock_relay_sync() {
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-sync"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let enqueue_receipt = sdk
.trades()
@@ -3527,11 +3533,11 @@ async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_confli
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-conflict-a"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("first target relays")
.try_with_idempotency_key("order-submit-conflict-idempotency")
.expect("first idempotency key");
@@ -3544,11 +3550,11 @@ async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_confli
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-conflict-b"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("second target relays")
.try_with_idempotency_key("order-submit-conflict-idempotency")
.expect("second idempotency key");
@@ -3636,11 +3642,11 @@ async fn order_submit_runtime_dtos_serialize_deterministically() {
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-serialized-enqueue"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("relay targets")
.with_idempotency_key(
SdkIdempotencyKey::new("order-serialized-idempotency").expect("idempotency"),
@@ -3653,8 +3659,22 @@ async fn order_submit_runtime_dtos_serialize_deterministically() {
enqueue_json["target_relays"],
serde_json::json!({
"kind": "explicit",
- "relays": [RELAY, RELAY_B],
- "canonical_relays": [RELAY_B, RELAY]
+ "targets": [
+ {
+ "kind": "Nostr",
+ "uri": RELAY,
+ "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ },
+ {
+ "kind": "Nostr",
+ "uri": RELAY_B,
+ "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05"
+ }
+ ],
+ "canonical_targets": [
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05",
+ "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ ]
})
);
assert_eq!(
@@ -3672,9 +3692,9 @@ async fn order_submit_runtime_dtos_serialize_deterministically() {
buyer_actor(),
listing_event_ptr(),
order_request("order-submit-try-idempotency"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("order-submit-try-key")
.expect("try idempotency key");
@@ -4300,10 +4320,10 @@ async fn order_request_evidence_ingest_stores_request_and_enables_decision_enque
.enqueue_prepared_decision_with_explicit_signer(
&actor,
plan,
- RelayResolutionPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays"),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -4643,11 +4663,11 @@ async fn order_decision_runtime_dtos_serialize_deterministically() {
seller_actor(),
request_event_ptr(&request_event),
order_decision("order-decision-serialized-enqueue"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("target relays")
.with_idempotency_key(
SdkIdempotencyKey::new("order-decision-serialized-idempotency").expect("idempotency"),
@@ -4660,8 +4680,22 @@ async fn order_decision_runtime_dtos_serialize_deterministically() {
enqueue_json["target_relays"],
serde_json::json!({
"kind": "explicit",
- "relays": [RELAY, RELAY_B],
- "canonical_relays": [RELAY_B, RELAY]
+ "targets": [
+ {
+ "kind": "Nostr",
+ "uri": RELAY,
+ "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ },
+ {
+ "kind": "Nostr",
+ "uri": RELAY_B,
+ "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05"
+ }
+ ],
+ "canonical_targets": [
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05",
+ "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ ]
})
);
assert_eq!(
@@ -4679,9 +4713,9 @@ async fn order_decision_runtime_dtos_serialize_deterministically() {
seller_actor(),
request_event_ptr(&request_event),
order_decision("order-decision-try-idempotency"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("order-decision-try-key")
.expect("try idempotency key");
@@ -4832,11 +4866,11 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
root_event.clone(),
previous_event.clone(),
proposal.clone(),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("proposal relays")
.with_idempotency_key(SdkIdempotencyKey::new("order-revision-proposal-dto").expect("key"))
.with_created_at(created_at);
@@ -4847,8 +4881,22 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
proposal_enqueue_json["target_relays"],
serde_json::json!({
"kind": "explicit",
- "relays": [RELAY, RELAY_B],
- "canonical_relays": [RELAY_B, RELAY]
+ "targets": [
+ {
+ "kind": "Nostr",
+ "uri": RELAY,
+ "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ },
+ {
+ "kind": "Nostr",
+ "uri": RELAY_B,
+ "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05"
+ }
+ ],
+ "canonical_targets": [
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05",
+ "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af"
+ ]
})
);
assert_eq!(
@@ -4862,9 +4910,9 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
root_event.clone(),
previous_event.clone(),
proposal.clone(),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("order-revision-proposal-try")
.expect("proposal try key");
@@ -4905,11 +4953,11 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
root_event.clone(),
previous_event.clone(),
revision_decision,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("decision relays")
.with_idempotency_key(
SdkIdempotencyKey::new("order-revision-decision-dto").expect("decision idempotency"),
@@ -4934,9 +4982,9 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
&previous_event_id,
RadrootsOrderRevisionOutcome::Accepted,
),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("order-revision-decision-try")
.expect("decision try key");
@@ -4966,11 +5014,11 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
root_event.clone(),
previous_event.clone(),
cancellation,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("cancellation relays")
.with_idempotency_key(
SdkIdempotencyKey::new("order-cancellation-dto").expect("cancellation idempotency"),
@@ -4995,9 +5043,9 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() {
root_event.clone(),
previous_event.clone(),
order_cancellation("order-revision-dto"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("order-cancellation-try")
.expect("cancellation try key");
@@ -5037,11 +5085,11 @@ async fn order_decision_enqueue_accept_stores_event_queues_outbox_and_updates_st
seller_actor(),
request_event_ptr(&request_event),
order_decision("order-decision-accept"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays")
.try_with_idempotency_key("order-decision-accept-idempotency")
.expect("idempotency");
@@ -5144,11 +5192,11 @@ async fn order_decision_enqueue_decline_stores_event_and_status_sees_declined()
seller_actor(),
request_event_ptr(&request_event),
decision,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let receipt = sdk
@@ -5188,11 +5236,11 @@ async fn order_decision_enqueue_rejects_missing_request_evidence_before_mutation
seller_actor(),
missing_request,
order_decision("order-decision-missing-request"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let error = sdk
@@ -5236,11 +5284,11 @@ async fn order_decision_enqueue_returns_sanitized_signer_errors_before_decision_
seller_actor(),
request_event_ptr(&request_event),
order_decision("order-decision-wrong-signer"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let error = sdk
@@ -5291,11 +5339,11 @@ async fn order_decision_enqueue_rejects_existing_decision_state_before_mutation(
seller_actor(),
request_event_ptr(&request_event),
decline,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("target relays");
let error = sdk
@@ -5359,10 +5407,10 @@ async fn order_revision_lifecycle_accepts_proposal_and_waits_for_rhi() {
.enqueue_prepared_revision_proposal_with_explicit_signer(
&proposal_actor,
proposal_plan,
- RelayResolutionPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("proposal target relays"),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
Some(
SdkIdempotencyKey::new("order-lifecycle-revision-proposal")
.expect("proposal idempotency"),
@@ -5410,10 +5458,10 @@ async fn order_revision_lifecycle_accepts_proposal_and_waits_for_rhi() {
.enqueue_prepared_revision_decision_with_explicit_signer(
&revision_decision_actor,
revision_decision_plan,
- RelayResolutionPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("revision decision target relays"),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&FixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -5501,11 +5549,11 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
request_event_ptr(&request_event),
request_event_ptr(&request_event),
proposal,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("proposal target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -5544,11 +5592,11 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
seller_actor(),
request_event_ptr(&request_event),
order_decision("order-lifecycle-pending-revision"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("decision target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -5572,11 +5620,11 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
request_event_ptr(&request_event),
order_event_ptr(&proposal_receipt.signed_event_id),
blocked_proposal,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("blocked proposal target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -5619,11 +5667,11 @@ async fn order_declined_revision_finalizes_declined_negotiation() {
request_event_ptr(&request_event),
request_event_ptr(&request_event),
proposal.clone(),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("proposal target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -5644,11 +5692,11 @@ async fn order_declined_revision_finalizes_declined_negotiation() {
request_event_ptr(&request_event),
order_event_ptr(&proposal_receipt.signed_event_id),
declined_revision,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("declined revision target relays"),
&FixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -5694,11 +5742,11 @@ async fn order_declined_revision_finalizes_declined_negotiation() {
request_event_ptr(&request_event),
order_event_ptr(&proposal_receipt.signed_event_id),
second_decision,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("second decision target relays"),
&FixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -5743,10 +5791,10 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
.enqueue_prepared_cancellation_with_explicit_signer(
&cancellation_actor,
cancellation_plan,
- RelayResolutionPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("cancellation target relays"),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
Some(
SdkIdempotencyKey::new("order-lifecycle-cancel").expect("cancellation idempotency"),
),
@@ -5778,11 +5826,11 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
request_event_ptr(&request_event),
request_event_ptr(&request_event),
order_cancellation("order-lifecycle-cancel"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("replay target relays")
.try_with_idempotency_key("order-lifecycle-cancel")
.expect("replay idempotency"),
@@ -5834,11 +5882,11 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
&request_event_id,
&request_event_id,
),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("missing target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -5865,11 +5913,11 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
seller_actor(),
request_event_ptr(&request_event),
order_decision("order-lifecycle-invalid"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("decision target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -5892,11 +5940,11 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
request_event_ptr(&request_event),
order_event_ptr(&decision_receipt.signed_event_id),
revision_without_proposal,
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("revision decision target relays"),
&FixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -5915,11 +5963,11 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
request_event_ptr(&request_event),
order_event_ptr(&decision_receipt.signed_event_id),
order_cancellation("order-lifecycle-invalid"),
- RelayResolutionPolicy::ConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("cancellation target relays"),
&FixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -6771,7 +6819,7 @@ async fn trade_product_mutation_returns_structured_ambiguity() {
}],
explicit_trade_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
TradeEvidenceMode::LocalOnly,
))
.await
diff --git a/crates/sdk/tests/runtime_foundation.rs b/crates/sdk/tests/runtime_foundation.rs
@@ -2,26 +2,38 @@
use radroots_events::ids::RadrootsOrderId;
use radroots_sdk::{
- BackupRequest, IntegrityRequest, LISTING_PUBLISH_OPERATION_KIND, RadrootsClient,
- RadrootsSdkClock, RadrootsSdkError, RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind,
- RadrootsSdkRecoveryAction, RadrootsSdkStorageConfig, RadrootsSdkTimestamp, RestoreRequest,
- SDK_IDEMPOTENCY_KEY_MAX_LEN, SDK_RELAY_TARGET_MAX_COUNT, SdkBackupState, SdkBackupVerification,
+ BackupRequest, IntegrityRequest, LISTING_PUBLISH_OPERATION_KIND, NostrProfile,
+ NostrRelayUrlPolicy, RadrootsClient, RadrootsSdkClock, RadrootsSdkError, RadrootsSdkErrorClass,
+ RadrootsSdkGeoNamesErrorKind, RadrootsSdkRecoveryAction, RadrootsSdkStorageConfig,
+ RadrootsSdkTimestamp, RestoreRequest, SDK_IDEMPOTENCY_KEY_MAX_LEN,
+ SDK_TRANSPORT_TARGET_MAX_COUNT, SdkBackupState, SdkBackupVerification,
SdkEventStoreStorageStatus, SdkIdempotencyKey, SdkOutboxStorageStatus,
- SdkPrivateStoreStorageStatus, SdkRelayTargetPolicy, SdkRelayTargetSet, SdkRelayUrlPolicy,
- SdkRestoreState, SdkSqliteStoreStatus, SdkSqliteWalCheckpointReceipt, SdkSqliteWalStatus,
- SdkStorageKind, StorageCheckpointReceipt, StorageCheckpointRequest, StorageStatusReceipt,
- StorageStatusRequest,
+ SdkPrivateStoreStorageStatus, SdkRestoreState, SdkSqliteStoreStatus,
+ SdkSqliteWalCheckpointReceipt, SdkSqliteWalStatus, SdkStorageKind, StorageCheckpointReceipt,
+ StorageCheckpointRequest, StorageStatusReceipt, StorageStatusRequest, TargetPolicy, TargetSet,
+ TransportProfile,
};
use radroots_trade::identity::RadrootsTradeLocator;
use sqlx::Row;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use std::path::{Path, PathBuf};
+fn nostr_profile<I, S>(
+ relays: I,
+ policy: NostrRelayUrlPolicy,
+) -> Result<TransportProfile, RadrootsSdkError>
+where
+ I: IntoIterator<Item = S>,
+ S: AsRef<str>,
+{
+ Ok(TransportProfile::nostr(NostrProfile::new(relays, policy)?))
+}
+
#[tokio::test]
async fn sdk_builder_defaults_to_memory_storage_and_no_relays() {
let sdk = RadrootsClient::builder().build().await.expect("sdk");
- assert!(sdk.relay_urls().is_empty());
+ assert!(sdk.configured_nostr_relay_urls().is_empty());
assert!(sdk.storage_paths().is_none());
let _listings = sdk.listings();
let _market = sdk.market();
@@ -34,15 +46,23 @@ async fn sdk_builder_defaults_to_memory_storage_and_no_relays() {
#[tokio::test]
async fn sdk_builder_validates_configured_relay_targets() {
let sdk = RadrootsClient::builder()
- .relay_url(" wss://relay-b.example.com/ ")
- .relay_url("wss://relay-a.example.com")
- .relay_url("wss://relay-a.example.com")
+ .transport_profile(
+ nostr_profile(
+ [
+ " wss://relay-b.example.com/ ",
+ "wss://relay-a.example.com",
+ "wss://relay-a.example.com",
+ ],
+ NostrRelayUrlPolicy::Public,
+ )
+ .expect("profile"),
+ )
.build()
.await
.expect("sdk");
assert_eq!(
- sdk.relay_urls(),
+ sdk.configured_nostr_relay_urls(),
&[
"wss://relay-b.example.com".to_owned(),
"wss://relay-a.example.com".to_owned()
@@ -52,23 +72,20 @@ async fn sdk_builder_validates_configured_relay_targets() {
#[tokio::test]
async fn sdk_builder_rejects_ws_relay_without_localhost_policy() {
- let result = RadrootsClient::builder()
- .relay_url("ws://127.0.0.1:8080")
- .build()
- .await;
+ let result = nostr_profile(["ws://127.0.0.1:8080"], NostrRelayUrlPolicy::Public);
match result {
Err(RadrootsSdkError::InvalidRelayUrl { .. }) => {}
- Err(error) => panic!("unexpected builder error: {error}"),
- Ok(_) => panic!("builder accepted ws relay without localhost policy"),
+ Err(error) => panic!("unexpected profile error: {error}"),
+ Ok(_) => panic!("profile accepted ws relay without localhost policy"),
}
}
#[test]
fn invalid_relay_url_errors_redact_userinfo() {
- let error = SdkRelayTargetSet::new(
+ let error = TargetSet::new(
["wss://user:password@relay.example.com/path?token=secret#frag"],
- SdkRelayUrlPolicy::Public,
+ NostrRelayUrlPolicy::Public,
)
.expect_err("invalid relay");
let message = error.to_string();
@@ -98,32 +115,31 @@ fn invalid_relay_url_errors_redact_userinfo() {
#[tokio::test]
async fn sdk_builder_allows_only_local_ws_targets_with_localhost_policy() {
let sdk = RadrootsClient::builder()
- .relay_url_policy(SdkRelayUrlPolicy::Localhost)
- .relay_url("ws://localhost:8080")
- .relay_url("ws://127.0.0.1:8081")
- .relay_url("ws://[::1]:8082")
+ .transport_profile(
+ nostr_profile(
+ [
+ "ws://localhost:8080",
+ "ws://127.0.0.1:8081",
+ "ws://[::1]:8082",
+ ],
+ NostrRelayUrlPolicy::Localhost,
+ )
+ .expect("profile"),
+ )
.build()
.await
.expect("sdk");
- assert_eq!(sdk.relay_urls().len(), 3);
+ assert_eq!(sdk.configured_nostr_relay_urls().len(), 3);
- let result = RadrootsClient::builder()
- .relay_url_policy(SdkRelayUrlPolicy::Localhost)
- .relay_url("ws://relay.example.com")
- .build()
- .await;
+ let result = nostr_profile(["ws://relay.example.com"], NostrRelayUrlPolicy::Localhost);
assert!(matches!(
result,
Err(RadrootsSdkError::InvalidRelayUrl { .. })
));
- let result = RadrootsClient::builder()
- .relay_url_policy(SdkRelayUrlPolicy::Localhost)
- .relay_url("ws://192.168.1.10:8080")
- .build()
- .await;
+ let result = nostr_profile(["ws://192.168.1.10:8080"], NostrRelayUrlPolicy::Localhost);
assert!(matches!(
result,
@@ -396,7 +412,7 @@ fn sdk_error_contract_methods_cover_all_variants() {
vec![RadrootsSdkRecoveryAction::ConfigureRelayTargets],
),
(
- SdkRelayTargetSet::new(["wss://u:p@relay.example.com"], SdkRelayUrlPolicy::Public)
+ TargetSet::new(["wss://u:p@relay.example.com"], NostrRelayUrlPolicy::Public)
.expect_err("invalid relay"),
"invalid_relay_url",
RadrootsSdkErrorClass::Configuration,
@@ -588,54 +604,68 @@ fn sdk_error_contract_methods_cover_all_variants() {
#[test]
fn relay_target_set_validates_normalizes_dedupes_preserves_order_and_caps() {
- let targets = SdkRelayTargetSet::new(
+ let targets = TargetSet::new(
[
" wss://relay-b.example.com/ ",
"wss://relay-a.example.com",
"wss://relay-a.example.com",
],
- SdkRelayUrlPolicy::Public,
+ NostrRelayUrlPolicy::Public,
)
.expect("targets");
assert_eq!(
- targets.relays(),
+ targets.nostr_relay_urls(),
&[
"wss://relay-b.example.com".to_owned(),
"wss://relay-a.example.com".to_owned()
]
);
assert_eq!(
- targets.canonical_relays(),
+ targets.canonical_targets(),
&[
- "wss://relay-a.example.com".to_owned(),
- "wss://relay-b.example.com".to_owned()
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05".to_owned(),
+ "fc957b234632cc52e2be19cba88bc85c69966ee5a2df61742b5875ff717fd6fa".to_owned()
]
);
assert_eq!(
- serde_json::to_value(SdkRelayTargetPolicy::explicit(targets.clone()))
+ serde_json::to_value(TargetPolicy::explicit(targets.clone()))
.expect("relay target policy json"),
serde_json::json!({
"kind": "explicit",
- "relays": ["wss://relay-b.example.com", "wss://relay-a.example.com"],
- "canonical_relays": ["wss://relay-a.example.com", "wss://relay-b.example.com"]
+ "targets": [
+ {
+ "kind": "Nostr",
+ "uri": "wss://relay-b.example.com",
+ "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05"
+ },
+ {
+ "kind": "Nostr",
+ "uri": "wss://relay-a.example.com",
+ "fingerprint": "fc957b234632cc52e2be19cba88bc85c69966ee5a2df61742b5875ff717fd6fa"
+ }
+ ],
+ "canonical_targets": [
+ "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05",
+ "fc957b234632cc52e2be19cba88bc85c69966ee5a2df61742b5875ff717fd6fa"
+ ]
})
);
assert!(matches!(
- SdkRelayTargetSet::new(Vec::<String>::new(), SdkRelayUrlPolicy::Public),
+ TargetSet::new(Vec::<String>::new(), NostrRelayUrlPolicy::Public),
Err(RadrootsSdkError::EmptyTargetRelays { .. })
));
- let too_many = (0..=SDK_RELAY_TARGET_MAX_COUNT)
+ let too_many = (0..=SDK_TRANSPORT_TARGET_MAX_COUNT)
.map(|index| format!("wss://relay-{index}.example.com"))
.collect::<Vec<_>>();
assert!(matches!(
- SdkRelayTargetSet::new(too_many, SdkRelayUrlPolicy::Public),
+ TargetSet::new(too_many, NostrRelayUrlPolicy::Public),
Err(RadrootsSdkError::RelayTargetLimitExceeded {
- max: SDK_RELAY_TARGET_MAX_COUNT,
+ max: SDK_TRANSPORT_TARGET_MAX_COUNT,
actual
- }) if actual == SDK_RELAY_TARGET_MAX_COUNT + 1
+ }) if actual == SDK_TRANSPORT_TARGET_MAX_COUNT + 1
));
}
@@ -995,9 +1025,9 @@ fn sdk_examples_stay_on_product_api_boundary() {
let local_enqueue = include_str!("../examples/sdk_v1_local_enqueue_and_mock_sync.rs");
assert!(local_enqueue.contains("RadrootsClient::builder()"));
assert!(local_enqueue.contains("ListingPreparePublishRequest"));
- assert!(local_enqueue.contains("SdkRelayTargetPolicy"));
- assert!(local_enqueue.contains("SdkRelayTargetSet"));
- assert!(local_enqueue.contains("SdkRelayUrlPolicy::Localhost"));
+ assert!(local_enqueue.contains("TargetPolicy"));
+ assert!(local_enqueue.contains("TargetSet"));
+ assert!(local_enqueue.contains("NostrRelayUrlPolicy::Localhost"));
assert!(local_enqueue.contains("RadrootsSdkLocalKeySigner"));
assert!(local_enqueue.contains("RadrootsSdkSignerProvider::LocalKey"));
assert!(local_enqueue.contains("enqueue_prepared_publish"));
diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs
@@ -239,9 +239,9 @@ const REQUIRED_IDENTITY_STORAGE_EXPORTS: &[&str] = &[
];
const REQUIRED_TRADE_POLICY_EXPORTS: &[&str] = &[
- "AckPolicy",
+ "SatisfactionPolicy",
"PublishMode",
- "RelayResolutionPolicy",
+ "TargetPolicy",
"SdkTradeIdempotencyRecord",
"PrivacyPreflightConfirmation",
"PrivacyPreflightReceipt",
diff --git a/crates/sdk/tests/sync_runtime.rs b/crates/sdk/tests/sync_runtime.rs
@@ -17,21 +17,22 @@ use radroots_events::{
listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct},
};
use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState, RadrootsOutboxOperationInput};
-use radroots_relay_transport::{
- RadrootsMockRelayPublishAdapter, RadrootsRelayOutcome, RadrootsRelayPublishAdapter,
- RadrootsRelayPublishRelayReceipt, RadrootsRelayPublishRequest, RadrootsRelayTransportError,
-};
+#[cfg(feature = "radrootsd-proxy")]
+use radroots_sdk::ProxyProfile;
use radroots_sdk::{
BackupRequest, IntegrityRequest, LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest,
- ListingPreparePublishRequest, PUSH_OUTBOX_DEFAULT_CLAIM_TTL_MS, PUSH_OUTBOX_DEFAULT_LIMIT,
+ ListingPreparePublishRequest, NostrProfile, NostrRelayUrlPolicy,
+ PUSH_OUTBOX_DEFAULT_CLAIM_TTL_MS, PUSH_OUTBOX_DEFAULT_LIMIT,
PUSH_OUTBOX_DEFAULT_NEXT_ATTEMPT_DELAY_MS, PUSH_OUTBOX_MAX_LIMIT, PushOutboxEventReceipt,
PushOutboxEventState, PushOutboxReceipt, PushOutboxRelayOutcomeKind, PushOutboxRelayReceipt,
PushOutboxRequest, RadrootsClient, RadrootsSdkError, RadrootsSdkTimestamp, RestoreRequest,
- SdkBackupManifestKind, SdkRelayAuthPolicy, SdkRelayTargetPolicy, SdkRelayUrlPolicy,
- SdkRestoreState, StorageStatusRequest, SyncStatusRequest, SyncStatusSource,
+ SdkBackupManifestKind, SdkRelayAuthPolicy, SdkRestoreState, StorageStatusRequest,
+ SyncStatusRequest, SyncStatusSource, TargetPolicy, TransportProfile,
+};
+use radroots_transport_nostr::{
+ RadrootsMockRelayPublishAdapter, RadrootsRelayOutcome, RadrootsRelayPublishAdapter,
+ RadrootsRelayPublishRelayReceipt, RadrootsRelayPublishRequest, RadrootsRelayTransportError,
};
-#[cfg(feature = "radrootsd-proxy")]
-use radroots_sdk::{SdkPublishTransport, adapters::radrootsd::RadrootsdProxyConfig};
#[cfg(feature = "radrootsd-proxy")]
use std::io::{Read, Write};
#[cfg(feature = "radrootsd-proxy")]
@@ -436,8 +437,11 @@ async fn directory_sdk(relays: &[&str]) -> (tempfile::TempDir, RadrootsClient) {
let mut builder = RadrootsClient::builder()
.directory_storage(tempdir.path().join("sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000));
- for relay in relays {
- builder = builder.relay_url(*relay);
+ if !relays.is_empty() {
+ builder = builder.transport_profile(TransportProfile::nostr(
+ NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ ));
}
let sdk = builder.build().await.expect("sdk");
(tempdir, sdk)
@@ -446,15 +450,35 @@ async fn directory_sdk(relays: &[&str]) -> (tempfile::TempDir, RadrootsClient) {
async fn system_clock_directory_sdk(relays: &[&str]) -> (tempfile::TempDir, RadrootsClient) {
let tempdir = tempfile::tempdir().expect("tempdir");
let mut builder = RadrootsClient::builder().directory_storage(tempdir.path().join("sdk"));
- for relay in relays {
- builder = builder.relay_url(*relay);
+ if !relays.is_empty() {
+ builder = builder.transport_profile(TransportProfile::nostr(
+ NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ ));
}
let sdk = builder.build().await.expect("sdk");
(tempdir, sdk)
}
async fn enqueue_listing(sdk: &RadrootsClient, d_tag: &str, title: &str, relays: &[&str]) -> i64 {
- enqueue_listing_with_policy(sdk, d_tag, title, relays, SdkRelayUrlPolicy::Public).await
+ enqueue_listing_with_policy(sdk, d_tag, title, relays, NostrRelayUrlPolicy::Public).await
+}
+
+fn delivery_plan_for_relays<I, S>(
+ relays: I,
+ policy: NostrRelayUrlPolicy,
+) -> radroots_outbox::RadrootsOutboxDeliveryPlanInput
+where
+ I: IntoIterator<Item = S>,
+ S: AsRef<str>,
+{
+ let target_set = radroots_sdk::TargetSet::nostr_relays(relays, policy).expect("target set");
+ radroots_outbox::RadrootsOutboxDeliveryPlanInput::new(
+ "explicit",
+ 1,
+ radroots_transport::RadrootsTransportSatisfactionPolicy::AllTargets,
+ target_set.into_targets(),
+ )
}
async fn backup_source(sdk: &RadrootsClient, root: &Path, name: &str) -> PathBuf {
@@ -483,14 +507,14 @@ async fn enqueue_listing_with_policy(
d_tag: &str,
title: &str,
relays: &[&str],
- url_policy: SdkRelayUrlPolicy,
+ url_policy: NostrRelayUrlPolicy,
) -> i64 {
sdk.listings()
.enqueue_publish_with_explicit_signer(
ListingEnqueuePublishRequest::new(
actor(),
listing(d_tag, title),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_target_relays(relays, url_policy)
.expect("relay targets"),
@@ -1425,9 +1449,7 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved
let sdk = RadrootsClient::builder()
.directory_storage(tempdir.path().join("sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
- .publish_transport(SdkPublishTransport::RadrootsdProxy(
- RadrootsdProxyConfig::new(endpoint),
- ))
+ .transport_profile(TransportProfile::proxy(ProxyProfile::new(endpoint)))
.build()
.await
.expect("sdk");
@@ -1438,7 +1460,7 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Proxy Coffee"),
- SdkRelayTargetPolicy::use_publish_transport(),
+ TargetPolicy::use_transport_profile(),
),
&FixtureSigner::new(SELLER),
)
@@ -1499,11 +1521,11 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() {
]);
let tempdir = tempfile::tempdir().expect("tempdir");
let storage = tempdir.path().join("sdk");
- let transport = SdkPublishTransport::RadrootsdProxy(RadrootsdProxyConfig::new(endpoint));
+ let transport = TransportProfile::proxy(ProxyProfile::new(endpoint));
let sdk = RadrootsClient::builder()
.directory_storage(storage.clone())
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
- .publish_transport(transport.clone())
+ .transport_profile(transport.clone())
.build()
.await
.expect("sdk");
@@ -1514,7 +1536,7 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() {
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Retry Coffee"),
- SdkRelayTargetPolicy::use_publish_transport(),
+ TargetPolicy::use_transport_profile(),
),
&FixtureSigner::new(SELLER),
)
@@ -1543,7 +1565,7 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() {
let sdk = RadrootsClient::builder()
.directory_storage(storage)
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_001))
- .publish_transport(transport)
+ .transport_profile(transport)
.build()
.await
.expect("reopened sdk");
@@ -1598,9 +1620,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out
let retryable_sdk = RadrootsClient::builder()
.directory_storage(tempdir.path().join("retryable-sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
- .publish_transport(SdkPublishTransport::RadrootsdProxy(
- RadrootsdProxyConfig::new(closed_endpoint).with_timeout(Duration::from_millis(50)),
- ))
+ .transport_profile(TransportProfile::proxy(ProxyProfile::new(closed_endpoint)))
.build()
.await
.expect("retryable sdk");
@@ -1610,7 +1630,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Proxy Error Coffee"),
- SdkRelayTargetPolicy::use_publish_transport(),
+ TargetPolicy::use_transport_profile(),
),
&FixtureSigner::new(SELLER),
)
@@ -1651,9 +1671,9 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out
let terminal_sdk = RadrootsClient::builder()
.directory_storage(tempdir.path().join("terminal-sdk"))
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
- .publish_transport(SdkPublishTransport::RadrootsdProxy(
- RadrootsdProxyConfig::new(terminal_endpoint),
- ))
+ .transport_profile(TransportProfile::proxy(ProxyProfile::new(
+ terminal_endpoint,
+ )))
.build()
.await
.expect("terminal sdk");
@@ -1663,7 +1683,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_B_D_TAG, "Terminal Coffee"),
- SdkRelayTargetPolicy::use_publish_transport(),
+ TargetPolicy::use_transport_profile(),
),
&FixtureSigner::new(SELLER),
)
@@ -1702,7 +1722,7 @@ fn push_outbox_contract_dtos_serialize_deterministically() {
.with_outbox_event_id(7)
.republish_accepted_relays(true)
.with_accepted_quorum(1)
- .with_relay_url_policy(SdkRelayUrlPolicy::Localhost)
+ .with_relay_url_policy(NostrRelayUrlPolicy::Localhost)
.with_auth_policy(SdkRelayAuthPolicy::DetectOnly)
.with_claim_ttl_ms(1_000)
.with_next_attempt_delay_ms(2_000);
@@ -1939,7 +1959,7 @@ async fn push_outbox_default_public_policy_rejects_queued_localhost_ws_targets()
LISTING_A_D_TAG,
"Local Coffee",
&[LOCAL_RELAY_A],
- SdkRelayUrlPolicy::Localhost,
+ NostrRelayUrlPolicy::Localhost,
)
.await;
let adapter = RadrootsMockRelayPublishAdapter::new();
@@ -1962,7 +1982,7 @@ async fn push_outbox_with_adapter_accepts_explicit_queued_localhost_ws_targets()
LISTING_A_D_TAG,
"Local Coffee",
&[LOCAL_RELAY_A, LOCAL_RELAY_B, LOCAL_RELAY_C],
- SdkRelayUrlPolicy::Localhost,
+ NostrRelayUrlPolicy::Localhost,
)
.await;
let adapter = RadrootsMockRelayPublishAdapter::new();
@@ -1973,7 +1993,7 @@ async fn push_outbox_with_adapter_accepts_explicit_queued_localhost_ws_targets()
&adapter,
PushOutboxRequest::new()
.with_limit(1)
- .with_relay_url_policy(SdkRelayUrlPolicy::Localhost),
+ .with_relay_url_policy(NostrRelayUrlPolicy::Localhost),
)
.await
.expect("push");
@@ -2016,9 +2036,9 @@ fn enqueue_publish_rejects_nonlocal_ws_relay_targets() {
let error = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_C_D_TAG, "Nonlocal Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([NONLOCAL_WS_RELAY], SdkRelayUrlPolicy::Localhost)
+ .try_with_target_relays([NONLOCAL_WS_RELAY], NostrRelayUrlPolicy::Localhost)
.expect_err("nonlocal ws relay target");
assert!(matches!(error, RadrootsSdkError::InvalidRelayUrl { .. }));
@@ -2026,9 +2046,9 @@ fn enqueue_publish_rejects_nonlocal_ws_relay_targets() {
let error = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_C_D_TAG, "Private LAN Coffee"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([PRIVATE_LAN_WS_RELAY], SdkRelayUrlPolicy::Localhost)
+ .try_with_target_relays([PRIVATE_LAN_WS_RELAY], NostrRelayUrlPolicy::Localhost)
.expect_err("private LAN ws relay target");
assert!(matches!(error, RadrootsSdkError::InvalidRelayUrl { .. }));
@@ -2260,7 +2280,7 @@ async fn push_outbox_does_not_claim_unsigned_outbox_work() {
.enqueue_operation(RadrootsOutboxOperationInput::new(
LISTING_PUBLISH_OPERATION_KIND,
prepared.frozen_draft,
- vec![RELAY_A.to_owned()],
+ delivery_plan_for_relays([RELAY_A], NostrRelayUrlPolicy::Public),
1_700_000_000_000,
))
.await
diff --git a/crates/sdk/tests/trade_product_publish_runtime.rs b/crates/sdk/tests/trade_product_publish_runtime.rs
@@ -21,10 +21,9 @@ use radroots_events::{
};
use radroots_nostr::prelude::{RadrootsNostrKeys, RadrootsNostrSecretKey};
use radroots_sdk::{
- AckPolicy, PublishMode, PushOutboxRelayOutcomeKind, RadrootsClient, RadrootsSdkLocalKeySigner,
- RadrootsSdkSignerProvider, RadrootsSdkTimestamp, RelayResolutionPolicy, SdkPublishTransport,
- SdkRelayTargetSet, SdkRelayUrlPolicy, TradeMutationOutcome, TradeProposeRequest,
- adapters::radrootsd::RadrootsdProxyConfig,
+ NostrRelayUrlPolicy, ProxyProfile, PublishMode, PushOutboxRelayOutcomeKind, RadrootsClient,
+ RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SatisfactionPolicy,
+ TargetPolicy, TargetSet, TradeMutationOutcome, TradeProposeRequest, TransportProfile,
};
use std::{
io::{Read, Write},
@@ -207,7 +206,7 @@ fn order_request(raw_order_id: &str) -> RadrootsOrderRequest {
fn trade_propose_request(
raw_order_id: &str,
publish_mode: PublishMode,
- ack_policy: AckPolicy,
+ ack_policy: SatisfactionPolicy,
) -> TradeProposeRequest {
let order = order_request(raw_order_id);
TradeProposeRequest::new(
@@ -224,9 +223,9 @@ fn trade_propose_request(
)
}
-fn explicit_trade_relays() -> RelayResolutionPolicy {
- RelayResolutionPolicy::explicit(
- SdkRelayTargetSet::new([RELAY], SdkRelayUrlPolicy::Public).expect("target relays"),
+fn explicit_trade_relays() -> TargetPolicy {
+ TargetPolicy::explicit(
+ TargetSet::new([RELAY], NostrRelayUrlPolicy::Public).expect("target relays"),
)
}
@@ -242,9 +241,7 @@ async fn trade_product_propose_enqueue_and_publish_uses_ack_policy() {
.signer_provider(RadrootsSdkSignerProvider::LocalKey(
RadrootsSdkLocalKeySigner::new(signer_keys).expect("local signer"),
))
- .publish_transport(SdkPublishTransport::RadrootsdProxy(
- RadrootsdProxyConfig::new(endpoint),
- ))
+ .transport_profile(TransportProfile::proxy(ProxyProfile::new(endpoint)))
.build()
.await
.expect("sdk");
@@ -256,7 +253,7 @@ async fn trade_product_propose_enqueue_and_publish_uses_ack_policy() {
trade_propose_request(
"trade-product-publish",
PublishMode::EnqueueAndPublish,
- AckPolicy::AtLeastOneRelay,
+ SatisfactionPolicy::AtLeastOneTarget,
)
.try_with_idempotency_key("trade-product-publish")
.expect("idempotency"),
@@ -284,7 +281,6 @@ async fn trade_product_propose_enqueue_and_publish_uses_ack_policy() {
let recorded = handle.join().expect("proxy request");
let body: serde_json::Value = serde_json::from_str(recorded.body.as_str()).expect("body");
assert_eq!(body["method"], "publish.event");
- assert_eq!(body["params"]["delivery_policy"]["mode"], "quorum");
- assert_eq!(body["params"]["delivery_policy"]["quorum"], 1);
+ assert_eq!(body["params"]["delivery_policy"]["mode"], "any");
assert_eq!(body["params"]["relays"], serde_json::json!([RELAY]));
}
diff --git a/crates/sdk/tests/unit/adapters_radrootsd_tests.rs b/crates/sdk/tests/unit/adapters_radrootsd_tests.rs
@@ -3,7 +3,7 @@ use radroots_publish_proxy_protocol::{
PublishJobStatus, PublishJobView, PublishRelayOutcome, PublishRelayOutcomeKind,
PublishRelaySource,
};
-use radroots_relay_transport::{
+use radroots_transport_nostr::{
RadrootsRelayPublishRequest, RadrootsRelayTargetSet, RadrootsRelayUrlPolicy,
};
use std::io::{Read, Write};
@@ -508,10 +508,22 @@ async fn publish_event_empty_http_error_reports_empty_body() {
#[tokio::test]
async fn relay_publish_adapter_derives_delivery_policy_and_timeout() {
- for (target_count, quorum, expected_policy) in [
- (2, 2, PublishDeliveryPolicy::All),
- (2, 1, PublishDeliveryPolicy::Any),
- (3, 2, PublishDeliveryPolicy::Quorum { quorum: 2 }),
+ for (target_count, satisfaction_policy, expected_policy) in [
+ (
+ 2,
+ radroots_transport::RadrootsTransportSatisfactionPolicy::AllTargets,
+ PublishDeliveryPolicy::All,
+ ),
+ (
+ 2,
+ radroots_transport::RadrootsTransportSatisfactionPolicy::AnyTarget,
+ PublishDeliveryPolicy::Any,
+ ),
+ (
+ 3,
+ radroots_transport::RadrootsTransportSatisfactionPolicy::AtLeast(2),
+ PublishDeliveryPolicy::Quorum { quorum: 2 },
+ ),
] {
let response_body = publish_response_json();
let (endpoint, handle) = spawn_http_server("200 OK", response_body.as_str());
@@ -527,7 +539,7 @@ async fn relay_publish_adapter_derives_delivery_policy_and_timeout() {
let receipts = adapter
.publish(
RadrootsRelayPublishRequest::new(signed_event(), targets, 10)
- .with_accepted_quorum(quorum),
+ .with_satisfaction_policy(satisfaction_policy),
)
.await
.expect("adapter publish");
@@ -569,7 +581,7 @@ async fn relay_publish_adapter_maps_proxy_errors_to_transport_errors() {
assert!(matches!(
error,
- radroots_relay_transport::RadrootsRelayTransportError::Transport(message)
+ radroots_transport_nostr::RadrootsRelayTransportError::Transport(message)
if message.contains("radrootsd")
));
}
diff --git a/crates/sdk/tests/unit/dvm_runtime_tests.rs b/crates/sdk/tests/unit/dvm_runtime_tests.rs
@@ -4,8 +4,8 @@ use super::{
dvm_trade_transition_proof_plan, sdk_timestamp_ms,
};
use crate::{
- RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkRelayTargetPolicy,
- SdkRelayUrlPolicy, SyncProjectionRefreshRequest,
+ NostrRelayUrlPolicy, RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey,
+ SyncProjectionRefreshRequest, TargetPolicy,
};
use radroots_authority::RadrootsActorContext;
use radroots_events::{
@@ -230,9 +230,9 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() {
let idempotency_len = idempotency.as_str().len();
let request = DvmTradeTransitionProofEnqueueRequest::from_prepare(
prepare.clone(),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY], NostrRelayUrlPolicy::Public)
.expect("relays")
.with_idempotency_key(idempotency)
.with_inventory_sequence(11)
@@ -254,7 +254,7 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() {
event_id('2'),
event_id('3'),
inventory_bins(),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("dvm-proof-request-2")
.expect("idempotency")
@@ -267,15 +267,15 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() {
assert!(matches!(
DvmTradeTransitionProofEnqueueRequest::from_prepare(
prepare,
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays(["ws://relay.example.com"], SdkRelayUrlPolicy::Public),
+ .try_with_target_relays(["ws://relay.example.com"], NostrRelayUrlPolicy::Public),
Err(RadrootsSdkError::InvalidRelayUrl { .. })
));
assert!(matches!(
DvmTradeTransitionProofEnqueueRequest::from_prepare(
proof_request(service_actor()),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key(""),
Err(RadrootsSdkError::InvalidRequest { .. })
diff --git a/crates/sdk/tests/unit/error_tests.rs b/crates/sdk/tests/unit/error_tests.rs
@@ -131,7 +131,7 @@ fn listing_and_store_errors_convert_to_sdk_error_classes() {
#[test]
fn outbox_error_conversion_handles_empty_targets_and_fallbacks() {
assert!(matches!(
- RadrootsSdkError::from(radroots_outbox::RadrootsOutboxError::EmptyTargetRelays),
+ RadrootsSdkError::from(radroots_outbox::RadrootsOutboxError::EmptyDeliveryTargets),
RadrootsSdkError::EmptyTargetRelays { ref operation } if operation == "outbox enqueue"
));
@@ -162,7 +162,7 @@ fn outbox_error_conversion_handles_empty_targets_and_fallbacks() {
#[test]
fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
let unsupported = RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::UnsupportedRelayScheme {
+ radroots_transport_nostr::RadrootsRelayTransportError::UnsupportedRelayScheme {
url: "ftp://user:secret@relay.example.com/path?token=secret".to_owned(),
scheme: "ftp".to_owned(),
},
@@ -176,7 +176,7 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::EmptyRelayHost {
+ radroots_transport_nostr::RadrootsRelayTransportError::EmptyRelayHost {
url: "wss://".to_owned(),
},
),
@@ -185,7 +185,7 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlQueryOrFragment {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlQueryOrFragment {
url: "wss://relay.example.com?token=secret".to_owned(),
},
),
@@ -194,7 +194,7 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlUserinfo {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlUserinfo {
url: "wss://user:secret@relay.example.com".to_owned(),
},
),
@@ -204,7 +204,7 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::WsRequiresLocalhostPolicy {
+ radroots_transport_nostr::RadrootsRelayTransportError::WsRequiresLocalhostPolicy {
url: "ws://relay.example.com".to_owned(),
},
),
@@ -213,7 +213,7 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlForbiddenDestination {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlForbiddenDestination {
url: "ws://127.0.0.1:9000".to_owned(),
reason: "localhost disabled".to_owned(),
},
@@ -223,7 +223,7 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlResolvedForbiddenDestination {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlResolvedForbiddenDestination {
url: "ws://relay.example.com".to_owned(),
address: "127.0.0.1".to_owned(),
reason: "loopback disabled".to_owned(),
@@ -234,13 +234,13 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::EmptyTargetSet
+ radroots_transport_nostr::RadrootsRelayTransportError::EmptyTargetSet
),
RadrootsSdkError::EmptyTargetRelays { ref operation } if operation == "relay publish"
));
assert!(matches!(
RadrootsSdkError::from(
- radroots_relay_transport::RadrootsRelayTransportError::RelayUrlParse {
+ radroots_transport_nostr::RadrootsRelayTransportError::RelayUrlParse {
url: "wss://user:secret@relay.example.com/path?token=secret".to_owned(),
reason: "bad relay URL".to_owned(),
},
@@ -250,13 +250,13 @@ fn relay_transport_error_conversion_redacts_and_classifies_url_errors() {
&& reason == "bad relay URL"
));
assert!(matches!(
- RadrootsSdkError::from(radroots_relay_transport::RadrootsRelayTransportError::Outbox(
- radroots_outbox::RadrootsOutboxError::EmptyTargetRelays,
+ RadrootsSdkError::from(radroots_transport_nostr::RadrootsRelayTransportError::Outbox(
+ radroots_outbox::RadrootsOutboxError::EmptyDeliveryTargets,
)),
RadrootsSdkError::EmptyTargetRelays { ref operation } if operation == "outbox enqueue"
));
assert!(matches!(
- RadrootsSdkError::from(radroots_relay_transport::RadrootsRelayTransportError::Transport(
+ RadrootsSdkError::from(radroots_transport_nostr::RadrootsRelayTransportError::Transport(
"offline".to_owned(),
)),
RadrootsSdkError::RelayTransport { ref message } if message == "Relay transport error: offline"
diff --git a/crates/sdk/tests/unit/farms_runtime_tests.rs b/crates/sdk/tests/unit/farms_runtime_tests.rs
@@ -167,9 +167,9 @@ fn farm_runtime_request_builders_and_serializers_cover_success_paths() {
let enqueue = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_B_D_TAG, "Queued Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY_A, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY_A, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("relay targets")
.with_idempotency_key(SdkIdempotencyKey::new("farm-unit-key").expect("key"))
.with_created_at(created_at);
@@ -182,7 +182,7 @@ fn farm_runtime_request_builders_and_serializers_cover_success_paths() {
let try_key = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_C_D_TAG, "Try Key Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("farm-unit-try-key")
.expect("try key");
@@ -357,15 +357,15 @@ fn farm_request_builders_reject_invalid_options_and_timestamp_bounds() {
let invalid_relays = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_A_D_TAG, "Invalid Relay Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays(["http://relay.radroots.test"], SdkRelayUrlPolicy::Public);
+ .try_with_target_relays(["http://relay.radroots.test"], NostrRelayUrlPolicy::Public);
assert!(invalid_relays.is_err());
let invalid_key = FarmEnqueuePublishRequest::new(
farmer_actor(),
farm(FARM_B_D_TAG, "Invalid Key Farm"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("");
assert!(invalid_key.is_err());
@@ -531,7 +531,7 @@ async fn farm_enqueue_publish_reports_prepare_errors_before_signing() {
FarmEnqueuePublishRequest::new(
farmer_actor(),
farm("AAAAAAAAAAAAAAAAAAAAA!", "Invalid Enqueue Farm"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
),
&FixtureSigner::new(FARMER),
@@ -556,7 +556,7 @@ async fn farm_client_enqueue_methods_cover_source_attached_workflow_paths() {
FarmEnqueuePublishRequest::new(
actor.clone(),
farm(FARM_A_D_TAG, "Enqueued Farm"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
)
.try_with_idempotency_key("farm-source-attached-enqueue")
@@ -580,7 +580,7 @@ async fn farm_client_enqueue_methods_cover_source_attached_workflow_paths() {
.enqueue_prepared_publish_with_explicit_signer(
&actor,
plan,
- SdkRelayTargetPolicy::try_explicit([RELAY_B], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_B], NostrRelayUrlPolicy::Public)
.expect("prepared target relays"),
None,
&signer,
@@ -612,7 +612,7 @@ async fn farm_configured_local_signer_enqueues_publish_without_explicit_signer()
FarmEnqueuePublishRequest::new(
actor,
farm(FARM_C_D_TAG, "Configured Farm"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
)
.try_with_idempotency_key("farm-configured-local")
@@ -646,7 +646,7 @@ async fn farm_configured_enqueue_reports_prepare_and_signer_errors() {
.enqueue_publish(FarmEnqueuePublishRequest::new(
actor.clone(),
farm("AAAAAAAAAAAAAAAAAAAAA!", "Invalid Configured Farm"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
))
.await,
@@ -671,7 +671,7 @@ async fn farm_configured_enqueue_reports_prepare_and_signer_errors() {
.enqueue_prepared_publish(
&actor,
plan,
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
None,
)
diff --git a/crates/sdk/tests/unit/listings_runtime_tests.rs b/crates/sdk/tests/unit/listings_runtime_tests.rs
@@ -104,9 +104,9 @@ fn listing_runtime_request_builders_and_serializers_cover_success_paths() {
let enqueue = ListingEnqueuePublishRequest::from_document(
actor(),
RadrootsListingDraftDocumentV1::new(listing(LISTING_B_D_TAG, "Queued Greens")),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
- .try_with_target_relays([RELAY_A, RELAY_B], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays([RELAY_A, RELAY_B], NostrRelayUrlPolicy::Public)
.expect("relay targets")
.with_idempotency_key(SdkIdempotencyKey::new("listing-unit-key").expect("key"))
.with_created_at(created_at);
@@ -119,7 +119,7 @@ fn listing_runtime_request_builders_and_serializers_cover_success_paths() {
let try_key = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_C_D_TAG, "Try Key Greens"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("listing-unit-try-key")
.expect("try key");
@@ -134,7 +134,7 @@ fn listing_request_builders_reject_invalid_options_and_timestamp_bounds() {
let invalid_key = ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Invalid Key Greens"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
+ TargetPolicy::UseConfiguredProfile,
)
.try_with_idempotency_key("");
assert!(invalid_key.is_err());
@@ -235,7 +235,7 @@ async fn listing_enqueue_publish_reports_prepare_errors_before_signing() {
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Future Enqueue Greens"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
)
.with_created_at(RadrootsSdkTimestamp::from_unix_seconds(u64::MAX)),
@@ -264,7 +264,7 @@ async fn listing_client_enqueue_methods_cover_source_attached_workflow_paths() {
ListingEnqueuePublishRequest::new(
actor.clone(),
listing(LISTING_A_D_TAG, "Enqueued Greens"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
)
.try_with_idempotency_key("listing-source-attached-enqueue")
@@ -288,7 +288,7 @@ async fn listing_client_enqueue_methods_cover_source_attached_workflow_paths() {
.enqueue_prepared_publish_with_explicit_signer(
&actor,
plan,
- SdkRelayTargetPolicy::try_explicit([RELAY_B], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_B], NostrRelayUrlPolicy::Public)
.expect("prepared target relays"),
None,
&signer,
@@ -320,7 +320,7 @@ async fn listing_configured_local_signer_enqueues_publish_without_explicit_signe
ListingEnqueuePublishRequest::new(
actor,
listing_for_seller(seller.as_str(), LISTING_C_D_TAG, "Configured Greens"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
)
.try_with_idempotency_key("listing-configured-local")
@@ -347,7 +347,7 @@ async fn listing_configured_enqueue_reports_missing_signer_after_prepare() {
ListingEnqueuePublishRequest::new(
actor.clone(),
listing(LISTING_A_D_TAG, "Configured Prepare Error Greens"),
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
)
.with_created_at(RadrootsSdkTimestamp::from_unix_seconds(u64::MAX)),
@@ -368,7 +368,7 @@ async fn listing_configured_enqueue_reports_missing_signer_after_prepare() {
.enqueue_prepared_publish(
&actor,
plan,
- SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ TargetPolicy::try_nostr_relays([RELAY_A], NostrRelayUrlPolicy::Public)
.expect("target relays"),
None,
)
diff --git a/crates/sdk/tests/unit/orders_runtime_tests.rs b/crates/sdk/tests/unit/orders_runtime_tests.rs
@@ -411,8 +411,8 @@ fn fixture_cancellation(raw_order_id: &str) -> RadrootsOrderCancellation {
}
}
-fn fixture_target_relays() -> RelayResolutionPolicy {
- RelayResolutionPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public).expect("target relays")
+fn fixture_target_relays() -> TargetPolicy {
+ TargetPolicy::try_nostr_relays([RELAY], NostrRelayUrlPolicy::Public).expect("target relays")
}
async fn prepared_order_sdk() -> RadrootsClient {
@@ -446,7 +446,7 @@ async fn order_configured_local_signer_enqueues_submit_without_explicit_signer()
fixture_order_request("order-configured-local-1"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await
.expect("enqueue submit");
@@ -467,7 +467,7 @@ async fn order_configured_local_signer_enqueues_lifecycle_wrappers_without_expli
fixture_order_decision("order-configured-decision"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await
.expect("configured decision");
@@ -489,7 +489,7 @@ async fn order_configured_local_signer_enqueues_lifecycle_wrappers_without_expli
proposal_payload,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await
.expect("configured revision proposal");
@@ -514,7 +514,7 @@ async fn order_configured_local_signer_enqueues_lifecycle_wrappers_without_expli
revision_proposal_payload.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -533,7 +533,7 @@ async fn order_configured_local_signer_enqueues_lifecycle_wrappers_without_expli
revision_decision_payload,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await
.expect("configured revision decision");
@@ -557,7 +557,7 @@ async fn order_configured_local_signer_enqueues_lifecycle_wrappers_without_expli
fixture_cancellation("order-configured-cancel"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await
.expect("configured cancellation");
@@ -585,7 +585,7 @@ async fn enqueue_fixture_submit(sdk: &RadrootsClient, raw_order_id: &str) -> Tra
plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2075,7 +2075,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
RadrootsOrderRevisionOutcome::Accepted,
);
let cancellation_payload = cancellation_payload();
- let policy = RelayResolutionPolicy::ConfiguredRelays;
+ let policy = TargetPolicy::UseConfiguredProfile;
assert_error_display(
TradeSubmitEnqueueRequest::new(
@@ -2084,9 +2084,9 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
submit_payload.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(Vec::<String>::new(), SdkRelayUrlPolicy::Public),
+ .try_with_target_relays(Vec::<String>::new(), NostrRelayUrlPolicy::Public),
"target relays",
);
assert_error_display(
@@ -2096,7 +2096,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
submit_payload,
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key(""),
"idempotency key",
@@ -2109,9 +2109,9 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
decision_payload.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(Vec::<String>::new(), SdkRelayUrlPolicy::Public),
+ .try_with_target_relays(Vec::<String>::new(), NostrRelayUrlPolicy::Public),
"target relays",
);
assert_error_display(
@@ -2121,7 +2121,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
decision_payload,
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key(" leading"),
"idempotency key",
@@ -2135,9 +2135,9 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
proposal_payload.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(Vec::<String>::new(), SdkRelayUrlPolicy::Public),
+ .try_with_target_relays(Vec::<String>::new(), NostrRelayUrlPolicy::Public),
"target relays",
);
assert_error_display(
@@ -2148,7 +2148,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
proposal_payload.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("trailing "),
"idempotency key",
@@ -2162,9 +2162,9 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
revision_decision_payload.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(Vec::<String>::new(), SdkRelayUrlPolicy::Public),
+ .try_with_target_relays(Vec::<String>::new(), NostrRelayUrlPolicy::Public),
"target relays",
);
assert_error_display(
@@ -2175,7 +2175,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
revision_decision_payload,
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key("invalid\nkey"),
"idempotency key",
@@ -2189,9 +2189,9 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
cancellation_payload.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(Vec::<String>::new(), SdkRelayUrlPolicy::Public),
+ .try_with_target_relays(Vec::<String>::new(), NostrRelayUrlPolicy::Public),
"target relays",
);
assert_error_display(
@@ -2202,7 +2202,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
cancellation_payload,
policy,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.try_with_idempotency_key(""),
"idempotency key",
@@ -2212,24 +2212,29 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() {
#[test]
fn trade_enqueue_policy_rejects_publish_modes_without_matching_side_effects() {
assert!(matches!(
- validate_trade_enqueue_policy(PublishMode::DryRun, AckPolicy::NoWait),
+ validate_trade_enqueue_policy(PublishMode::DryRun, SatisfactionPolicy::NoWait),
Err(RadrootsSdkError::InvalidRequest { ref message })
if message == "trade dry-run publish mode must use a prepare request"
));
assert!(matches!(
- validate_trade_enqueue_policy(PublishMode::EnqueueOnly, AckPolicy::AtLeastOneRelay),
+ validate_trade_enqueue_policy(PublishMode::EnqueueOnly, SatisfactionPolicy::AtLeastOneTarget),
Err(RadrootsSdkError::InvalidRequest { ref message })
if message == "trade enqueue-only publish mode only supports no-wait acknowledgement"
));
assert!(matches!(
- validate_trade_enqueue_policy(PublishMode::EnqueueAndPublish, AckPolicy::NoWait),
+ validate_trade_enqueue_policy(PublishMode::EnqueueAndPublish, SatisfactionPolicy::NoWait),
Err(RadrootsSdkError::InvalidRequest { ref message })
if message == "trade enqueue-and-publish requires a relay acknowledgement policy"
));
- assert!(validate_trade_enqueue_policy(PublishMode::EnqueueOnly, AckPolicy::NoWait).is_ok());
assert!(
- validate_trade_enqueue_policy(PublishMode::EnqueueAndPublish, AckPolicy::AtLeastOneRelay)
- .is_ok()
+ validate_trade_enqueue_policy(PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait).is_ok()
+ );
+ assert!(
+ validate_trade_enqueue_policy(
+ PublishMode::EnqueueAndPublish,
+ SatisfactionPolicy::AtLeastOneTarget
+ )
+ .is_ok()
);
}
@@ -2347,7 +2352,7 @@ async fn prepared_submit_and_decision_enqueue_cover_source_attached_success_path
decision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
Some(SdkIdempotencyKey::new("prepared-decision").expect("idempotency")),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2385,7 +2390,7 @@ async fn prepared_revision_lifecycle_enqueue_cover_source_attached_success_paths
proposal_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
Some(SdkIdempotencyKey::new("prepared-proposal").expect("idempotency")),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2415,7 +2420,7 @@ async fn prepared_revision_lifecycle_enqueue_cover_source_attached_success_paths
revision_decision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2449,7 +2454,7 @@ async fn prepared_cancellation_enqueue_covers_source_attached_success_path() {
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
Some(SdkIdempotencyKey::new("prepared-cancellation").expect("idempotency")),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2474,7 +2479,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
fixture_order_request("order-wrapper-decision"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2489,7 +2494,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
fixture_order_decision("order-wrapper-decision"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2506,7 +2511,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
fixture_order_request("order-wrapper-revision"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2527,7 +2532,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
proposal_payload.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2543,7 +2548,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
fixture_revision_decision(&proposal_payload, &proposal.signed_event_id),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2560,7 +2565,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
fixture_order_request("order-wrapper-cancellation"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2576,7 +2581,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
fixture_cancellation("order-wrapper-cancellation"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2615,7 +2620,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
decision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2642,7 +2647,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
proposal_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2669,7 +2674,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
revision_decision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2696,7 +2701,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2760,7 +2765,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
closed_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2774,7 +2779,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
closed_proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -2791,7 +2796,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
closed_revision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2808,7 +2813,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
closed_cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -2850,7 +2855,7 @@ async fn configured_prepared_lifecycle_enqueues_run_preflight_guards() {
decision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -2876,7 +2881,7 @@ async fn configured_prepared_lifecycle_enqueues_run_preflight_guards() {
proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -2903,7 +2908,7 @@ async fn configured_prepared_lifecycle_enqueues_run_preflight_guards() {
revision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -2929,7 +2934,7 @@ async fn configured_prepared_lifecycle_enqueues_run_preflight_guards() {
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -2981,7 +2986,7 @@ async fn configured_prepared_lifecycle_enqueues_report_existing_event_lookup_err
decision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -2995,7 +3000,7 @@ async fn configured_prepared_lifecycle_enqueues_report_existing_event_lookup_err
proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await,
@@ -3030,7 +3035,7 @@ async fn configured_prepared_lifecycle_enqueues_report_existing_event_lookup_err
revision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3044,7 +3049,7 @@ async fn configured_prepared_lifecycle_enqueues_report_existing_event_lookup_err
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3073,7 +3078,7 @@ async fn configured_enqueue_wrappers_report_prepare_errors_before_signing() {
fixture_order_request("order-configured-prepare-submit"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await,
Err(RadrootsSdkError::UnauthorizedActor { .. })
@@ -3087,7 +3092,7 @@ async fn configured_enqueue_wrappers_report_prepare_errors_before_signing() {
fixture_order_decision("order-configured-prepare-decision"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await,
Err(RadrootsSdkError::UnauthorizedActor { .. })
@@ -3102,7 +3107,7 @@ async fn configured_enqueue_wrappers_report_prepare_errors_before_signing() {
proposal.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await,
Err(RadrootsSdkError::UnauthorizedActor { .. })
@@ -3117,7 +3122,7 @@ async fn configured_enqueue_wrappers_report_prepare_errors_before_signing() {
fixture_revision_decision(&proposal, &previous_event_id),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await,
Err(RadrootsSdkError::UnauthorizedActor { .. })
@@ -3132,7 +3137,7 @@ async fn configured_enqueue_wrappers_report_prepare_errors_before_signing() {
fixture_cancellation("order-configured-prepare-cancel"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
))
.await,
Err(RadrootsSdkError::UnauthorizedActor { .. })
@@ -3160,7 +3165,7 @@ async fn configured_prepared_methods_report_missing_configured_signer_after_pref
submit_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3183,7 +3188,7 @@ async fn configured_prepared_methods_report_missing_configured_signer_after_pref
decision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3212,7 +3217,7 @@ async fn configured_prepared_methods_report_missing_configured_signer_after_pref
proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3241,7 +3246,7 @@ async fn configured_prepared_methods_report_missing_configured_signer_after_pref
revision_proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3266,7 +3271,7 @@ async fn configured_prepared_methods_report_missing_configured_signer_after_pref
revision_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3290,7 +3295,7 @@ async fn configured_prepared_methods_report_missing_configured_signer_after_pref
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None
)
.await,
@@ -3400,7 +3405,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3436,7 +3441,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3461,7 +3466,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
revision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -3494,7 +3499,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -3527,7 +3532,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
decision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3540,7 +3545,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
decision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3563,7 +3568,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
decision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -3602,7 +3607,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
proposal_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3615,7 +3620,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
proposal_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3638,7 +3643,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
proposal_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -3677,7 +3682,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
proposal_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
@@ -3700,7 +3705,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
revision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -3713,7 +3718,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
revision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -3736,7 +3741,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
revision_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -3771,7 +3776,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
cancellation_plan.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
@@ -3784,7 +3789,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
cancellation_plan,
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
None,
)
.await
@@ -3834,7 +3839,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
fixture_order_request("order-wrapper-submit-error"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_created_at(out_of_range),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
@@ -3851,7 +3856,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
fixture_order_decision("order-wrapper-decision-error"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_created_at(out_of_range),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
@@ -3877,7 +3882,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
proposal_payload.clone(),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_created_at(out_of_range),
&OrderFixtureSigner::new(SELLER_SECRET_KEY_HEX),
@@ -3895,7 +3900,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
fixture_revision_decision(&proposal_payload, &previous_event_id),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_created_at(out_of_range),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
@@ -3913,7 +3918,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
fixture_cancellation("order-wrapper-cancellation-error"),
fixture_target_relays(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
.with_created_at(out_of_range),
&OrderFixtureSigner::new(BUYER_SECRET_KEY_HEX),
@@ -4024,7 +4029,7 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths()
reason: "not workable".to_owned(),
},
);
- let policy = RelayResolutionPolicy::ConfiguredRelays;
+ let policy = TargetPolicy::UseConfiguredProfile;
let submit_prepare =
TradeSubmitPrepareRequest::new(buyer_actor(), root_event.clone(), order_request_payload())
@@ -4041,9 +4046,9 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths()
order_request_payload(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(["wss://relay-a.radroots.test"], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays(["wss://relay-a.radroots.test"], NostrRelayUrlPolicy::Public)
.expect("submit relays")
.with_idempotency_key(SdkIdempotencyKey::new("submit-unit-key").expect("key"))
.with_created_at(created_at);
@@ -4070,9 +4075,9 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths()
order_decision_payload(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(["wss://relay-b.radroots.test"], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays(["wss://relay-b.radroots.test"], NostrRelayUrlPolicy::Public)
.expect("decision relays")
.with_idempotency_key(SdkIdempotencyKey::new("decision-unit-key").expect("key"))
.with_created_at(created_at);
@@ -4094,9 +4099,9 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths()
proposal.clone(),
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(["wss://relay-c.radroots.test"], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays(["wss://relay-c.radroots.test"], NostrRelayUrlPolicy::Public)
.expect("proposal relays")
.with_idempotency_key(SdkIdempotencyKey::new("proposal-unit-key").expect("key"))
.with_created_at(created_at);
@@ -4118,9 +4123,9 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths()
revision_decision,
policy.clone(),
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(["wss://relay-d.radroots.test"], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays(["wss://relay-d.radroots.test"], NostrRelayUrlPolicy::Public)
.expect("revision decision relays")
.with_idempotency_key(SdkIdempotencyKey::new("revision-decision-unit-key").expect("key"))
.with_created_at(created_at);
@@ -4142,9 +4147,9 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths()
cancellation_payload(),
policy,
PublishMode::EnqueueOnly,
- AckPolicy::NoWait,
+ SatisfactionPolicy::NoWait,
)
- .try_with_target_relays(["wss://relay-e.radroots.test"], SdkRelayUrlPolicy::Public)
+ .try_with_target_relays(["wss://relay-e.radroots.test"], NostrRelayUrlPolicy::Public)
.expect("cancellation relays")
.with_idempotency_key(SdkIdempotencyKey::new("cancellation-unit-key").expect("key"))
.with_created_at(created_at);
diff --git a/crates/sdk/tests/unit/relay_targets_tests.rs b/crates/sdk/tests/unit/relay_targets_tests.rs
@@ -1,201 +0,0 @@
-use super::{
- AckPolicy, PublishMode, RelayResolutionPolicy, SdkRelayTargetPolicy, SdkRelayTargetSet,
- SdkRelayUrlPolicy,
-};
-use crate::{RadrootsSdkError, SDK_RELAY_TARGET_MAX_COUNT};
-
-#[path = "../support/serializer_failure.rs"]
-mod serializer_failure;
-
-use serializer_failure::assert_struct_serialize_error_paths;
-
-#[test]
-fn publish_mode_and_ack_policy_serialize_explicit_product_contracts() {
- assert_eq!(
- serde_json::to_value(PublishMode::DryRun).expect("json"),
- serde_json::json!("dry_run")
- );
- assert_eq!(
- serde_json::to_value(PublishMode::EnqueueOnly).expect("json"),
- serde_json::json!("enqueue_only")
- );
- assert_eq!(
- serde_json::to_value(PublishMode::EnqueueAndPublish).expect("json"),
- serde_json::json!("enqueue_and_publish")
- );
- assert_eq!(
- serde_json::to_value(AckPolicy::NoWait).expect("json"),
- serde_json::json!("no_wait")
- );
- assert_eq!(
- serde_json::to_value(AckPolicy::AtLeastOneRelay).expect("json"),
- serde_json::json!("at_least_one_relay")
- );
- assert_eq!(
- serde_json::to_value(AckPolicy::AllRelays).expect("json"),
- serde_json::json!("all_relays")
- );
- assert_eq!(
- serde_json::to_value(AckPolicy::quorum(2).expect("quorum")).expect("json"),
- serde_json::json!({ "quorum": { "required": 2 } })
- );
- assert!(matches!(
- AckPolicy::quorum(0),
- Err(RadrootsSdkError::InvalidRequest { ref message })
- if message == "ack policy quorum must require at least one relay"
- ));
-}
-
-fn is_local_ws_relay(value: &str) -> bool {
- let Some(rest) = value.strip_prefix("ws://") else {
- return false;
- };
- let authority = rest
- .split_once('/')
- .map(|(authority, _)| authority)
- .unwrap_or(rest);
- let host = relay_authority_host(authority);
- matches!(host.as_deref(), Some("localhost" | "127.0.0.1" | "[::1]"))
-}
-
-fn relay_authority_host(authority: &str) -> Option<String> {
- if let Some(after_open) = authority.strip_prefix('[') {
- let close_index = after_open.find(']')?;
- return Some(format!("[{}]", &after_open[..close_index]));
- }
- Some(
- authority
- .split_once(':')
- .map(|(host, _)| host)
- .unwrap_or(authority)
- .to_owned(),
- )
-}
-
-#[test]
-fn use_configured_policy_serializes_as_kind_only() {
- let trade_policy = RelayResolutionPolicy::ConfiguredRelays;
- assert_eq!(
- serde_json::to_value(&trade_policy).expect("json"),
- serde_json::json!({ "kind": "configured_relays" })
- );
- assert_struct_serialize_error_paths(&trade_policy, 1);
-
- let policy = SdkRelayTargetPolicy::UseConfiguredRelays;
- assert_eq!(
- serde_json::to_value(&policy).expect("json"),
- serde_json::json!({ "kind": "use_configured_relays" })
- );
- assert_struct_serialize_error_paths(&policy, 1);
-
- let publish_transport_policy = SdkRelayTargetPolicy::use_publish_transport();
- assert_eq!(
- serde_json::to_value(&publish_transport_policy).expect("json"),
- serde_json::json!({ "kind": "use_publish_transport" })
- );
- assert_struct_serialize_error_paths(&publish_transport_policy, 1);
-}
-
-#[test]
-fn target_set_accessors_and_configured_relays_cover_empty_and_dedupe_paths() {
- assert_eq!(
- SdkRelayTargetSet::from_configured_relays(Vec::<String>::new(), SdkRelayUrlPolicy::Public)
- .expect("empty configured"),
- Vec::<String>::new()
- );
-
- let targets = SdkRelayTargetSet::from_normalized_relays(vec![
- "wss://relay-a.example.com".to_owned(),
- "wss://relay-a.example.com".to_owned(),
- "wss://relay-b.example.com".to_owned(),
- ])
- .expect("targets");
-
- assert_eq!(targets.len(), 2);
- assert!(!targets.is_empty());
- assert_struct_serialize_error_paths(&targets, 2);
- assert_struct_serialize_error_paths(&SdkRelayTargetPolicy::explicit(targets.clone()), 3);
- assert_eq!(
- serde_json::to_value(&targets).expect("targets json"),
- serde_json::json!({
- "relays": ["wss://relay-a.example.com", "wss://relay-b.example.com"],
- "canonical_relays": ["wss://relay-a.example.com", "wss://relay-b.example.com"]
- })
- );
- assert_eq!(
- targets.into_vec(),
- vec![
- "wss://relay-a.example.com".to_owned(),
- "wss://relay-b.example.com".to_owned()
- ]
- );
-
- assert_eq!(
- SdkRelayTargetPolicy::try_explicit(
- vec!["wss://relay-c.example.com".to_owned()],
- SdkRelayUrlPolicy::Public,
- )
- .expect("explicit policy"),
- SdkRelayTargetPolicy::Explicit(
- SdkRelayTargetSet::new(["wss://relay-c.example.com"], SdkRelayUrlPolicy::Public)
- .expect("target set"),
- )
- );
- assert_eq!(
- serde_json::to_value(
- RelayResolutionPolicy::try_explicit(
- vec!["wss://relay-c.example.com".to_owned()],
- SdkRelayUrlPolicy::Public,
- )
- .expect("trade explicit policy")
- )
- .expect("trade policy json"),
- serde_json::json!({
- "kind": "explicit",
- "relays": ["wss://relay-c.example.com"],
- "canonical_relays": ["wss://relay-c.example.com"]
- })
- );
-}
-
-#[test]
-fn normalized_relays_reject_empty_and_over_limit_sets() {
- assert!(matches!(
- SdkRelayTargetSet::from_normalized_relays(Vec::new()),
- Err(RadrootsSdkError::EmptyTargetRelays { .. })
- ));
-
- let too_many = (0..=SDK_RELAY_TARGET_MAX_COUNT)
- .map(|index| format!("wss://relay-{index}.example.com"))
- .collect::<Vec<_>>();
- assert!(matches!(
- SdkRelayTargetSet::from_normalized_relays(too_many),
- Err(RadrootsSdkError::RelayTargetLimitExceeded { actual, .. })
- if actual == SDK_RELAY_TARGET_MAX_COUNT + 1
- ));
-}
-
-#[test]
-fn local_ws_authority_parser_handles_ipv6_ports_and_non_ws_values() {
- assert!(is_local_ws_relay("ws://localhost:8080/path"));
- assert!(is_local_ws_relay("ws://127.0.0.1:8080"));
- assert!(is_local_ws_relay("ws://[::1]:8080"));
- assert!(!is_local_ws_relay("wss://relay.example.com"));
- assert!(!is_local_ws_relay("ws://relay.example.com"));
- assert!(matches!(
- SdkRelayTargetSet::new(["ws://relay.example.com"], SdkRelayUrlPolicy::Localhost),
- Err(RadrootsSdkError::InvalidRelayUrl { reason, .. })
- if reason.contains("localhost")
- ));
- assert!(matches!(
- SdkRelayTargetSet::new(["ws://relay.example.com"], SdkRelayUrlPolicy::Public),
- Err(RadrootsSdkError::InvalidRelayUrl { reason, .. })
- if reason.contains("localhost")
- ));
- assert_eq!(relay_authority_host("[::1]:8080"), Some("[::1]".to_owned()));
- assert_eq!(
- relay_authority_host("relay.example.com:443"),
- Some("relay.example.com".to_owned())
- );
- assert_eq!(relay_authority_host("[::1"), None);
-}
diff --git a/crates/sdk/tests/unit/runtime_tests.rs b/crates/sdk/tests/unit/runtime_tests.rs
@@ -71,6 +71,13 @@ fn assert_wal_checkpoint_complete(receipt: &SdkSqliteWalCheckpointReceipt) {
assert!(receipt.checkpoint_complete);
}
+fn nostr_profile(
+ relays: impl IntoIterator<Item = &'static str>,
+ policy: crate::NostrRelayUrlPolicy,
+) -> crate::TransportProfile {
+ crate::TransportProfile::nostr(crate::NostrProfile::new(relays, policy).expect("Nostr profile"))
+}
+
fn storage_status() -> StorageStatusReceipt {
StorageStatusReceipt {
storage: SdkStorageKind::Memory,
@@ -258,16 +265,20 @@ async fn private_store_validates_location_rows_and_round_trips_valid_records() {
#[test]
fn publish_transport_defaults_and_delegated_resolution_are_explicit() {
- let direct = SdkPublishTransport::default();
- assert_eq!(direct, SdkPublishTransport::DirectNostrRelay);
- assert!(!direct.supports_delegated_relay_resolution());
+ let local = TransportProfile::default();
+ assert_eq!(local, TransportProfile::LocalOnly);
+ assert!(!local.supports_delegated_target_resolution());
+
+ let nostr = nostr_profile(
+ ["wss://relay.example.com"],
+ crate::NostrRelayUrlPolicy::Public,
+ );
+ assert!(!nostr.supports_delegated_target_resolution());
#[cfg(feature = "radrootsd-proxy")]
{
- let proxy = SdkPublishTransport::RadrootsdProxy(RadrootsdProxyConfig::new(
- "http://127.0.0.1:9/rpc",
- ));
- assert!(proxy.supports_delegated_relay_resolution());
+ let proxy = TransportProfile::proxy(crate::ProxyProfile::new("http://127.0.0.1:9/rpc"));
+ assert!(proxy.supports_delegated_target_resolution());
}
}
@@ -284,8 +295,7 @@ async fn open_storage_and_storage_kind_cover_memory_directory_and_file_failures(
storage_paths: None,
geonames: None,
clock: RadrootsSdkClock::Fixed(RadrootsSdkTimestamp::from_unix_seconds(1)),
- relay_urls: Vec::new(),
- publish_transport: SdkPublishTransport::DirectNostrRelay,
+ transport_profile: TransportProfile::local_only(),
#[cfg(feature = "signer-adapters")]
signer_provider: None,
};
@@ -307,8 +317,7 @@ async fn open_storage_and_storage_kind_cover_memory_directory_and_file_failures(
storage_paths: Some(directory_paths),
geonames: None,
clock: RadrootsSdkClock::Fixed(RadrootsSdkTimestamp::from_unix_seconds(1)),
- relay_urls: Vec::new(),
- publish_transport: SdkPublishTransport::DirectNostrRelay,
+ transport_profile: TransportProfile::local_only(),
#[cfg(feature = "signer-adapters")]
signer_provider: None,
};
@@ -354,8 +363,10 @@ async fn runtime_public_surface_covers_builders_status_integrity_backup_and_rest
.clock(RadrootsSdkClock::Fixed(
RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000),
))
- .relay_url_policy(SdkRelayUrlPolicy::Localhost)
- .relay_url("ws://127.0.0.1:7777")
+ .transport_profile(nostr_profile(
+ ["ws://127.0.0.1:7777"],
+ crate::NostrRelayUrlPolicy::Localhost,
+ ))
.build()
.await
.expect("memory sdk");
@@ -363,7 +374,10 @@ async fn runtime_public_surface_covers_builders_status_integrity_backup_and_rest
memory_sdk.now().expect("fixed now").unix_seconds(),
1_700_000_000
);
- assert_eq!(memory_sdk.relay_urls(), ["ws://127.0.0.1:7777"]);
+ assert_eq!(
+ memory_sdk.configured_nostr_relay_urls(),
+ ["ws://127.0.0.1:7777"]
+ );
assert!(memory_sdk.storage_paths().is_none());
let _ = memory_sdk.farms();
let _ = memory_sdk.listings();
diff --git a/crates/sdk/tests/unit/sync_runtime_tests.rs b/crates/sdk/tests/unit/sync_runtime_tests.rs
@@ -1,7 +1,8 @@
#[cfg(feature = "radrootsd-proxy")]
use super::{
- CLAIM_OWNER, complete_proxy_publish_attempt, proxy_delivery_policy, proxy_error_message,
- proxy_outbox_idempotency_key, proxy_transport_error_receipt, push_proxy_claimed_outbox_event,
+ CLAIM_OWNER, complete_proxy_publish_attempt, proxy_delivery_policy_from_satisfaction,
+ proxy_error_message, proxy_outbox_idempotency_key, proxy_transport_error_receipt,
+ push_proxy_claimed_outbox_event,
};
use super::{
PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt, PushOutboxRelayOutcomeKind,
@@ -39,7 +40,9 @@ use radroots_outbox::RadrootsOutboxClaimedEvent;
use radroots_outbox::{RadrootsOutboxEventState, RadrootsOutboxStatusSummary};
#[cfg(feature = "radrootsd-proxy")]
use radroots_publish_proxy_protocol::PublishDeliveryPolicy;
-use radroots_relay_transport::{
+#[cfg(feature = "radrootsd-proxy")]
+use radroots_transport::RadrootsTransportSatisfactionPolicy;
+use radroots_transport_nostr::{
RadrootsRelayOutcomeKind, RadrootsRelayPublishAdapter, RadrootsRelayPublishReceipt,
RadrootsRelayPublishRelayReceipt, RadrootsRelayPublishRequest, RadrootsRelayTransportError,
};
@@ -139,11 +142,12 @@ async fn claimed_proxy_event(d_tag: &str) -> (crate::RadrootsClient, RadrootsOut
operation_kind: "sync.proxy.unit.v1",
actor: &actor,
frozen_draft: &draft,
- target_relays: crate::SdkRelayTargetPolicy::try_explicit(
+ target_relays: crate::TargetPolicy::try_nostr_relays(
["wss://relay.example.com"],
- crate::SdkRelayUrlPolicy::Public,
+ crate::NostrRelayUrlPolicy::Public,
)
.expect("target relays"),
+ satisfaction_policy: crate::SatisfactionPolicy::AllTargets,
idempotency_key: None,
},
&ProxyFixtureSigner::new(),
@@ -308,7 +312,7 @@ fn sync_status_summary_conversions_preserve_all_fields() {
let event_summary = RadrootsEventStoreStatusSummary {
total_events: 11,
projection_eligible_events: 7,
- relay_observations: 3,
+ transport_observations: 3,
last_event_seq: Some(9),
last_event_updated_at_ms: Some(1_700_000_000_000),
};
@@ -352,7 +356,7 @@ fn push_outbox_request_builders_validate_all_bounds() {
.with_outbox_event_id(9)
.republish_accepted_relays(true)
.with_accepted_quorum(2)
- .with_relay_url_policy(crate::SdkRelayUrlPolicy::Localhost)
+ .with_relay_url_policy(crate::NostrRelayUrlPolicy::Localhost)
.with_auth_policy(SdkRelayAuthPolicy::DetectOnly)
.with_claim_ttl_ms(7)
.with_next_attempt_delay_ms(11);
@@ -506,11 +510,26 @@ async fn proxy_push_empty_queue_and_private_helpers_are_deterministic() {
.expect("empty proxy push");
assert_eq!(receipt.attempted_events, 0);
- assert_eq!(proxy_delivery_policy(0, None), PublishDeliveryPolicy::Any);
- assert_eq!(proxy_delivery_policy(2, None), PublishDeliveryPolicy::All);
assert_eq!(
- proxy_delivery_policy(3, Some(2)),
- PublishDeliveryPolicy::Quorum { quorum: 2 }
+ proxy_delivery_policy_from_satisfaction(
+ 0,
+ &RadrootsTransportSatisfactionPolicy::AllTargets
+ )
+ .expect("zero-target proxy policy"),
+ PublishDeliveryPolicy::Any
+ );
+ assert_eq!(
+ proxy_delivery_policy_from_satisfaction(
+ 2,
+ &RadrootsTransportSatisfactionPolicy::AllTargets
+ )
+ .expect("all-target proxy policy"),
+ PublishDeliveryPolicy::All
+ );
+ assert_eq!(
+ proxy_delivery_policy_from_satisfaction(2, &RadrootsTransportSatisfactionPolicy::AnyTarget)
+ .expect("any-target proxy policy"),
+ PublishDeliveryPolicy::Any
);
assert_eq!(
proxy_outbox_idempotency_key(7, 3, "event-id"),
@@ -594,11 +613,11 @@ async fn proxy_push_reports_missing_signed_claim_before_daemon_publish() {
expected_event_id: "b".repeat(64),
},
signed_event: None,
- target_relays: vec!["wss://relay.example.com".to_owned()],
+ delivery_targets: Vec::new(),
};
assert!(matches!(
- push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, None, 60_000, 1_700_000_000_000)
+ push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000)
.await,
Err(RadrootsSdkError::RelayTransport { message })
if message.contains("Outbox claim 41 does not contain a signed event")
@@ -613,7 +632,7 @@ async fn proxy_claim_publish_marks_retryable_transport_errors() {
let adapter =
RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc"));
let receipt =
- push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, None, 60_000, 1_700_000_000_000)
+ push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000)
.await
.expect("transport error receipt");
@@ -706,9 +725,9 @@ trait RelayReceiptFixture {
impl RelayReceiptFixture for RadrootsRelayPublishReceipt {
fn with_relay(mut self) -> Self {
self.relays.push(
- radroots_relay_transport::RadrootsRelayPublishRelayReceipt::attempted(
+ radroots_transport_nostr::RadrootsRelayPublishRelayReceipt::attempted(
"wss://relay.example.com",
- radroots_relay_transport::RadrootsRelayOutcome::accepted(),
+ radroots_transport_nostr::RadrootsRelayOutcome::accepted(),
),
);
self
diff --git a/crates/sdk/tests/unit/transport_tests.rs b/crates/sdk/tests/unit/transport_tests.rs
@@ -0,0 +1,213 @@
+use super::{
+ NostrProfile, NostrRelayUrlPolicy, PublishMode, SatisfactionPolicy, TargetPolicy, TargetSet,
+ TransportProfile,
+};
+use crate::{RadrootsSdkError, SDK_TRANSPORT_TARGET_MAX_COUNT};
+
+#[path = "../support/serializer_failure.rs"]
+mod serializer_failure;
+
+use serializer_failure::assert_struct_serialize_error_paths;
+
+#[test]
+fn publish_mode_and_ack_policy_serialize_explicit_product_contracts() {
+ assert_eq!(
+ serde_json::to_value(PublishMode::DryRun).expect("json"),
+ serde_json::json!("dry_run")
+ );
+ assert_eq!(
+ serde_json::to_value(PublishMode::EnqueueOnly).expect("json"),
+ serde_json::json!("enqueue_only")
+ );
+ assert_eq!(
+ serde_json::to_value(PublishMode::EnqueueAndPublish).expect("json"),
+ serde_json::json!("enqueue_and_publish")
+ );
+ assert_eq!(
+ serde_json::to_value(SatisfactionPolicy::NoWait).expect("json"),
+ serde_json::json!("no_wait")
+ );
+ assert_eq!(
+ serde_json::to_value(SatisfactionPolicy::AtLeastOneTarget).expect("json"),
+ serde_json::json!("at_least_one_target")
+ );
+ assert_eq!(
+ serde_json::to_value(SatisfactionPolicy::AllTargets).expect("json"),
+ serde_json::json!("all_targets")
+ );
+ assert_eq!(
+ serde_json::to_value(SatisfactionPolicy::at_least(2).expect("satisfaction policy"))
+ .expect("json"),
+ serde_json::json!({ "at_least": { "required": 2 } })
+ );
+ assert!(matches!(
+ SatisfactionPolicy::at_least(0),
+ Err(RadrootsSdkError::InvalidRequest { ref message })
+ if message == "satisfaction policy must require at least one target"
+ ));
+}
+
+fn is_local_ws_relay(value: &str) -> bool {
+ let Some(rest) = value.strip_prefix("ws://") else {
+ return false;
+ };
+ let authority = rest
+ .split_once('/')
+ .map(|(authority, _)| authority)
+ .unwrap_or(rest);
+ let host = relay_authority_host(authority);
+ matches!(host.as_deref(), Some("localhost" | "127.0.0.1" | "[::1]"))
+}
+
+fn relay_authority_host(authority: &str) -> Option<String> {
+ if let Some(after_open) = authority.strip_prefix('[') {
+ let close_index = after_open.find(']')?;
+ return Some(format!("[{}]", &after_open[..close_index]));
+ }
+ Some(
+ authority
+ .split_once(':')
+ .map(|(host, _)| host)
+ .unwrap_or(authority)
+ .to_owned(),
+ )
+}
+
+#[test]
+fn use_configured_policy_serializes_as_kind_only() {
+ let policy = TargetPolicy::UseConfiguredProfile;
+ assert_eq!(
+ serde_json::to_value(&policy).expect("json"),
+ serde_json::json!({ "kind": "use_configured_profile" })
+ );
+ assert_struct_serialize_error_paths(&policy, 1);
+
+ let publish_transport_policy = TargetPolicy::use_transport_profile();
+ assert_eq!(
+ serde_json::to_value(&publish_transport_policy).expect("json"),
+ serde_json::json!({ "kind": "use_transport_profile" })
+ );
+ assert_struct_serialize_error_paths(&publish_transport_policy, 1);
+}
+
+#[test]
+fn target_set_accessors_and_configured_relays_cover_empty_and_dedupe_paths() {
+ assert!(
+ TransportProfile::local_only()
+ .configured_nostr_relay_urls()
+ .is_empty()
+ );
+
+ let targets = TargetSet::from_normalized_nostr_relays(vec![
+ "wss://relay-a.example.com".to_owned(),
+ "wss://relay-a.example.com".to_owned(),
+ "wss://relay-b.example.com".to_owned(),
+ ])
+ .expect("targets");
+
+ assert_eq!(targets.len(), 2);
+ assert!(!targets.is_empty());
+ assert_struct_serialize_error_paths(&targets, 2);
+ assert_struct_serialize_error_paths(&TargetPolicy::explicit(targets.clone()), 3);
+ let targets_json = serde_json::to_value(&targets).expect("targets json");
+ assert_eq!(
+ targets_json["targets"].as_array().expect("targets").len(),
+ 2
+ );
+ assert_eq!(
+ targets_json["canonical_targets"]
+ .as_array()
+ .expect("canonical targets")
+ .len(),
+ 2
+ );
+ assert_eq!(
+ targets.nostr_relay_urls(),
+ vec![
+ "wss://relay-a.example.com".to_owned(),
+ "wss://relay-b.example.com".to_owned()
+ ]
+ );
+
+ assert_eq!(
+ TargetPolicy::try_nostr_relays(
+ vec!["wss://relay-c.example.com".to_owned()],
+ NostrRelayUrlPolicy::Public,
+ )
+ .expect("explicit policy"),
+ TargetPolicy::Explicit(
+ TargetSet::new(["wss://relay-c.example.com"], NostrRelayUrlPolicy::Public)
+ .expect("target set"),
+ )
+ );
+ assert_eq!(
+ serde_json::to_value(
+ TargetPolicy::try_nostr_relays(
+ vec!["wss://relay-c.example.com".to_owned()],
+ NostrRelayUrlPolicy::Public,
+ )
+ .expect("trade explicit policy")
+ )
+ .expect("trade policy json"),
+ serde_json::json!({
+ "kind": "explicit",
+ "targets": [{
+ "kind": "Nostr",
+ "uri": "wss://relay-c.example.com",
+ "fingerprint": "ec4b5005dd1fcf0d949045e3d5524f9a6a95209ecc888f582ae2e9bf69e5b8e6"
+ }],
+ "canonical_targets": ["ec4b5005dd1fcf0d949045e3d5524f9a6a95209ecc888f582ae2e9bf69e5b8e6"]
+ })
+ );
+
+ let nostr_profile = TransportProfile::nostr(
+ NostrProfile::new(["wss://relay-d.example.com"], NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ );
+ assert_eq!(
+ nostr_profile.configured_nostr_relay_urls(),
+ vec!["wss://relay-d.example.com".to_owned()]
+ );
+}
+
+#[test]
+fn normalized_relays_reject_empty_and_over_limit_sets() {
+ assert!(matches!(
+ TargetSet::from_normalized_nostr_relays(Vec::new()),
+ Err(RadrootsSdkError::EmptyTargetRelays { .. })
+ ));
+
+ let too_many = (0..=SDK_TRANSPORT_TARGET_MAX_COUNT)
+ .map(|index| format!("wss://relay-{index}.example.com"))
+ .collect::<Vec<_>>();
+ assert!(matches!(
+ TargetSet::from_normalized_nostr_relays(too_many),
+ Err(RadrootsSdkError::RelayTargetLimitExceeded { actual, .. })
+ if actual == SDK_TRANSPORT_TARGET_MAX_COUNT + 1
+ ));
+}
+
+#[test]
+fn local_ws_authority_parser_handles_ipv6_ports_and_non_ws_values() {
+ assert!(is_local_ws_relay("ws://localhost:8080/path"));
+ assert!(is_local_ws_relay("ws://127.0.0.1:8080"));
+ assert!(is_local_ws_relay("ws://[::1]:8080"));
+ assert!(!is_local_ws_relay("wss://relay.example.com"));
+ assert!(!is_local_ws_relay("ws://relay.example.com"));
+ assert!(matches!(
+ TargetSet::new(["ws://relay.example.com"], NostrRelayUrlPolicy::Localhost),
+ Err(RadrootsSdkError::InvalidRelayUrl { reason, .. })
+ if reason.contains("localhost")
+ ));
+ assert!(matches!(
+ TargetSet::new(["ws://relay.example.com"], NostrRelayUrlPolicy::Public),
+ Err(RadrootsSdkError::InvalidRelayUrl { reason, .. })
+ if reason.contains("localhost")
+ ));
+ assert_eq!(relay_authority_host("[::1]:8080"), Some("[::1]".to_owned()));
+ assert_eq!(
+ relay_authority_host("relay.example.com:443"),
+ Some("relay.example.com".to_owned())
+ );
+ assert_eq!(relay_authority_host("[::1"), None);
+}
diff --git a/crates/sdk/tests/unit/workflow_runtime_tests.rs b/crates/sdk/tests/unit/workflow_runtime_tests.rs
@@ -84,6 +84,27 @@ fn signed_event() -> RadrootsSignedNostrEvent {
}
}
+fn nostr_profile(relay: &'static str) -> crate::TransportProfile {
+ crate::TransportProfile::nostr(
+ crate::NostrProfile::new([relay], crate::NostrRelayUrlPolicy::Public)
+ .expect("Nostr profile"),
+ )
+}
+
+fn workflow_delivery_plan() -> radroots_outbox::RadrootsOutboxDeliveryPlanInput {
+ let target_set = TargetSet::nostr_relays(
+ ["wss://relay.example.com"],
+ crate::NostrRelayUrlPolicy::Public,
+ )
+ .expect("target set");
+ radroots_outbox::RadrootsOutboxDeliveryPlanInput::new(
+ "explicit",
+ 1,
+ radroots_transport::RadrootsTransportSatisfactionPolicy::AllTargets,
+ target_set.into_targets(),
+ )
+}
+
#[test]
fn workflow_digest_and_event_helpers_cover_error_and_input_paths() {
assert_eq!(digest_prefix("abcdef1234567890"), "abcdef123456");
@@ -115,16 +136,15 @@ fn workflow_digest_and_event_helpers_cover_error_and_input_paths() {
"workflow.test.v1",
&draft,
signed_event(),
- vec!["wss://relay.example.com".to_owned()],
+ workflow_delivery_plan(),
idempotency_key,
- false,
true,
1_700_000_000_000,
);
assert_eq!(input.operation_kind, "workflow.test.v1");
assert_eq!(
- input.target_relays,
- vec!["wss://relay.example.com".to_owned()]
+ input.delivery_plan.targets[0].uri.as_str(),
+ "wss://relay.example.com"
);
assert!(input.event_store_inserted);
}
@@ -132,7 +152,7 @@ fn workflow_digest_and_event_helpers_cover_error_and_input_paths() {
#[tokio::test]
async fn enqueue_signed_workflow_stores_signed_event_and_reports_idempotency_conflicts() {
let sdk = crate::RadrootsClient::builder()
- .relay_url("wss://relay.example.com")
+ .transport_profile(nostr_profile("wss://relay.example.com"))
.fixed_clock(crate::RadrootsSdkTimestamp::from_unix_seconds(
1_700_000_010,
))
@@ -150,7 +170,8 @@ async fn enqueue_signed_workflow_stores_signed_event_and_reports_idempotency_con
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &first_draft,
- target_relays: SdkRelayTargetPolicy::UseConfiguredRelays,
+ target_relays: TargetPolicy::UseConfiguredProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: Some(idempotency_key.clone()),
},
&signer,
@@ -190,7 +211,8 @@ async fn enqueue_signed_workflow_stores_signed_event_and_reports_idempotency_con
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &second_draft,
- target_relays: SdkRelayTargetPolicy::UseConfiguredRelays,
+ target_relays: TargetPolicy::UseConfiguredProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: Some(idempotency_key),
},
&signer,
@@ -232,7 +254,7 @@ async fn enqueue_configured_signed_workflow_uses_sdk_signer_provider() {
let secret_key = RadrootsNostrSecretKey::from_hex(FARMER_SECRET_KEY_HEX).expect("secret key");
let keys = RadrootsNostrKeys::new(secret_key);
let sdk = crate::RadrootsClient::builder()
- .relay_url("wss://relay.example.com")
+ .transport_profile(nostr_profile("wss://relay.example.com"))
.fixed_clock(crate::RadrootsSdkTimestamp::from_unix_seconds(
1_700_000_011,
))
@@ -252,7 +274,8 @@ async fn enqueue_configured_signed_workflow_uses_sdk_signer_provider() {
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &draft,
- target_relays: SdkRelayTargetPolicy::UseConfiguredRelays,
+ target_relays: TargetPolicy::UseConfiguredProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: None,
},
)
@@ -266,7 +289,7 @@ async fn enqueue_configured_signed_workflow_uses_sdk_signer_provider() {
#[tokio::test]
async fn enqueue_signed_workflow_reports_outbox_preflight_failure_without_mutation() {
let sdk = crate::RadrootsClient::builder()
- .relay_url("wss://relay.example.com")
+ .transport_profile(nostr_profile("wss://relay.example.com"))
.build()
.await
.expect("sdk");
@@ -286,7 +309,8 @@ async fn enqueue_signed_workflow_reports_outbox_preflight_failure_without_mutati
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &draft,
- target_relays: SdkRelayTargetPolicy::UseConfiguredRelays,
+ target_relays: TargetPolicy::UseConfiguredProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: None,
};
@@ -312,7 +336,7 @@ async fn enqueue_signed_workflow_reports_store_failures() {
.expect("actor");
let draft = frozen_draft_for(FARMER_PUBLIC_KEY_HEX);
let closed_store_sdk = crate::RadrootsClient::builder()
- .relay_url("wss://relay.example.com")
+ .transport_profile(nostr_profile("wss://relay.example.com"))
.build()
.await
.expect("sdk");
@@ -321,7 +345,8 @@ async fn enqueue_signed_workflow_reports_store_failures() {
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &draft,
- target_relays: SdkRelayTargetPolicy::UseConfiguredRelays,
+ target_relays: TargetPolicy::UseConfiguredProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: None,
};
assert!(matches!(
@@ -339,7 +364,7 @@ async fn enqueue_signed_workflow_reports_store_failures() {
async fn enqueue_signed_workflow_reports_clock_failures() {
let sdk = crate::RadrootsClient::builder()
.clock(crate::RadrootsSdkClock::BeforeUnixEpoch)
- .relay_url("wss://relay.example.com")
+ .transport_profile(nostr_profile("wss://relay.example.com"))
.build()
.await
.expect("sdk");
@@ -350,7 +375,8 @@ async fn enqueue_signed_workflow_reports_clock_failures() {
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &draft,
- target_relays: SdkRelayTargetPolicy::UseConfiguredRelays,
+ target_relays: TargetPolicy::UseConfiguredProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: None,
};
assert!(matches!(
@@ -369,13 +395,14 @@ async fn enqueue_signed_workflow_rejects_publish_transport_targets_without_proxy
operation_kind: "workflow.test.v1",
actor: &actor,
frozen_draft: &draft,
- target_relays: SdkRelayTargetPolicy::UsePublishTransport,
+ target_relays: TargetPolicy::UseTransportProfile,
+ satisfaction_policy: SatisfactionPolicy::AllTargets,
idempotency_key: None,
};
assert!(matches!(
enqueue_signed_workflow(&sdk, request, &WorkflowSigner::new()).await,
Err(RadrootsSdkError::EmptyTargetRelays { operation })
- if operation == "publish transport relay resolution"
+ if operation == "publish transport profile"
));
}