commit 57dbb2e0d0ab3a08ec9adfc2059bdeb6647aab0d
parent d7609bf452d600f169cdfe70029df17ab91befd2
Author: triesap <tyson@radroots.org>
Date: Tue, 23 Jun 2026 23:52:07 +0000
sdk: wire configured signer workflows
- add configured signer enqueue paths for farm listing and order workflows
- keep explicit signer entrypoints under advanced method names
- expose Radroots product NIP-46 permission sets for Myc signing
- update SDK feature bundles docs examples and workflow coverage
Diffstat:
21 files changed, 1036 insertions(+), 332 deletions(-)
diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml
@@ -85,9 +85,8 @@ local-runtime = [
"serde",
"serde_json",
"runtime",
- "local-signer",
+ "signer-adapters",
"relay-runtime",
- "signing",
"relay-client",
]
local-runtime-radrootsd-proxy = [
@@ -95,8 +94,7 @@ local-runtime-radrootsd-proxy = [
"serde",
"serde_json",
"runtime",
- "local-signer",
- "signing",
+ "signer-adapters",
"radrootsd-proxy",
]
@@ -141,7 +139,12 @@ required-features = ["runtime"]
[[example]]
name = "sdk_v1_local_enqueue_and_mock_sync"
path = "examples/sdk_v1_local_enqueue_and_mock_sync.rs"
-required-features = ["runtime", "local-signer"]
+required-features = ["runtime", "signer-adapters"]
+
+[[example]]
+name = "sdk_v1_myc_nip46_signer_setup"
+path = "examples/sdk_v1_myc_nip46_signer_setup.rs"
+required-features = ["runtime", "signer-adapters"]
[[example]]
name = "runtime_local"
diff --git a/crates/sdk/README b/crates/sdk/README
@@ -3,7 +3,7 @@
Curated Rad Roots Rust SDK for local-first Rad Roots product workflows.
The SDK v1 product runtime is centered on `RadrootsSdk::builder()`,
-`sdk.listings()`, `sdk.sync()`, and `sdk.orders()`.
+`sdk.farms()`, `sdk.listings()`, `sdk.orders()`, and `sdk.sync()`.
`RadrootsSdk::builder()` defaults to memory storage, the system clock, no relay
URLs, and no production network publishing. Directory storage is opt-in and
@@ -11,14 +11,27 @@ creates `event_store.sqlite` and `outbox.sqlite` in the selected directory.
Configured relay URLs are also opt-in enqueue defaults used when a publish
request chooses `SdkRelayTargetPolicy::UseConfiguredRelays`.
+When `signer-adapters` is enabled, `RadrootsSdk::builder()` accepts a configured
+`RadrootsSdkSignerProvider`. The production signing modes are `local_key` and
+`myc_nip46`. Product enqueue methods use that configured provider, so callers do
+not pass signing internals through every farm, listing, or order write call.
+`signer_status()`, `configured_signer()`, and
+`sign_with_configured_signer(...)` expose the typed SDK signer boundary.
+
`sdk.listings().prepare_publish(...)` is side-effect-free and returns a typed
-`ListingPublishPlan`. `sdk.listings().enqueue_prepared_publish(...)` signs 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(...)`. The enqueue path uses typed relay target and
+`ListingPublishPlan`. With a configured signer,
+`sdk.listings().enqueue_prepared_publish(...)` signs 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 order write methods follow the same
+configured signer pattern. The enqueue path uses typed relay 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 product API.
+
Event `created_at` and local observation time are separate contracts. The event
timestamp remains the authored Nostr event timestamp, while event-store and
outbox mutation timestamps use the SDK clock at enqueue time. Listing enqueue
@@ -28,19 +41,25 @@ receipts report mutation state with the product names `StoredAndQueued` and
`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. `push_outbox_with_adapter(...)` remains
-available for tests and controlled adapter-level substrate checks.
-`radrootsd-proxy` adds the Radrootsd Publish Proxy transport for daemon-resolved
-publishing through `publish.event`.
+not require configured builder relays. Direct relay 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 `publish.event`.
The `local-runtime` feature is the curated feature bundle for local product
runtime consumers. It enables `std`, `serde`, `serde_json`, `runtime`,
-`local-signer`, `relay-runtime`, `signing`, and `relay-client`. `relay-client`
-is retained in this bundle only for current direct relay publish callers that
-have not migrated to the product runtime yet; it is classified for removal once
-those callers are SDK-runtime backed. `local-runtime` does not enable
-`radrootsd-proxy`; use `local-runtime-radrootsd-proxy` when the runtime should
-publish through Radrootsd instead of a direct relay transport.
+`signer-adapters`, `relay-runtime`, and `relay-client`. `signer-adapters`
+contains the SDK `local_key` and `myc_nip46` signing surface. `relay-client` is
+retained in this bundle only for direct relay publish callers. `local-runtime`
+does not enable `radrootsd-proxy`; use `local-runtime-radrootsd-proxy` when the
+runtime should publish through Radrootsd instead of a direct relay transport.
+Both bundles use the same configured signer provider API.
+
+`radroots_sdk_myc_nip46_product_permissions()` and
+`radroots_sdk_myc_nip46_product_permission_strings()` expose the SDK product
+NIP-46 `sign_event:<kind>` permission set for Myc connection setup. The set is
+derived from the farm, listing, and order 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`
@@ -61,8 +80,6 @@ explicit modules:
- `protocol::listing`
- `protocol::order`
- `protocol::identity` when `identity-models` is enabled
-- `client`
-- `config`
- `adapters`
Product runtime callers do not need `WireEventParts`. Wire helpers are exposed
@@ -74,15 +91,18 @@ Compile the SDK v1 product examples with:
```bash
cargo check -p radroots_sdk --example sdk_v1_listing_prepare --features runtime
-cargo check -p radroots_sdk --example sdk_v1_local_enqueue_and_mock_sync --features runtime,local-signer
+cargo check -p radroots_sdk --example sdk_v1_local_enqueue_and_mock_sync --features runtime,signer-adapters
+cargo check -p radroots_sdk --example sdk_v1_myc_nip46_signer_setup --features runtime,signer-adapters
```
`sdk_v1_listing_prepare` shows `RadrootsSdk::builder()`,
`ListingPreparePublishRequest`, and `ListingPublishPlan`.
`sdk_v1_local_enqueue_and_mock_sync` shows localhost relay target selection,
-prepared listing enqueue, `push_outbox_with_adapter(...)` with a mock relay
-adapter, and `OrderStatusRequest`. The examples stay on product APIs and do not
-use `WireEventParts`.
+configured local-key signing, prepared listing enqueue,
+`push_outbox_with_adapter(...)` with a mock relay adapter, and
+`OrderStatusRequest`. `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 `WireEventParts`.
SDK v1 does not claim strict NIP-99 Markdown-content interoperability. It emits
and consumes Rad Roots v1 listing and trade event contracts.
@@ -93,7 +113,7 @@ Optional advanced substrate is explicitly feature-scoped:
- `identity-storage`: encrypted identity-file helpers
- `signing`: Nostr builder and local signing adapters
- `relay-client`: relay client and publish adapters
-- `signer-adapters`: NIP-46 and signer-session primitives
+- `signer-adapters`: SDK local-key and Myc NIP-46 signer providers
The crate is licensed as `MIT OR Apache-2.0`. Its manifest is configured for a
future crates.io release, but a public release still requires the full SDK check
diff --git a/crates/sdk/examples/runtime_local.rs b/crates/sdk/examples/runtime_local.rs
@@ -1,85 +1,35 @@
-use radroots_authority::{
- RadrootsActorContext, RadrootsEventSigner, RadrootsSignerError, RadrootsSignerIdentity,
-};
+use radroots_authority::RadrootsActorContext;
use radroots_core::{
RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
RadrootsCoreQuantityPrice, RadrootsCoreUnit,
};
use radroots_events::contract::RadrootsActorRole;
-use radroots_events::draft::{
- RadrootsFrozenEventDraft, RadrootsSignedNostrEvent, RadrootsSignedNostrEventParts,
-};
use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId};
+use radroots_nostr::prelude::RadrootsNostrKeys;
use radroots_sdk::protocol::farm::RadrootsFarmRef;
use radroots_sdk::protocol::listing::{
RadrootsListing, RadrootsListingBin, RadrootsListingProduct,
};
use radroots_sdk::{
ListingPreparePublishRequest, OrderStatusRequest, PushOutboxRequest, RadrootsSdk,
- RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkRelayTargetPolicy,
- SdkRelayUrlPolicy,
+ RadrootsSdkError, RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp,
+ SdkIdempotencyKey, SdkRelayTargetPolicy, SdkRelayUrlPolicy,
};
-const SELLER: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const RELAY: &str = "wss://relay.example.com";
-#[derive(Clone)]
-struct FixtureSigner {
- identity: RadrootsSignerIdentity,
-}
-
-impl FixtureSigner {
- fn new(pubkey: &str) -> Self {
- Self {
- identity: RadrootsSignerIdentity::new(pubkey).expect("identity"),
- }
- }
-}
-
-impl RadrootsEventSigner for FixtureSigner {
- fn pubkey(&self) -> &radroots_events::ids::RadrootsPublicKey {
- self.identity.pubkey()
- }
-
- fn sign_frozen_draft(
- &self,
- draft: &RadrootsFrozenEventDraft,
- ) -> Result<RadrootsSignedNostrEvent, RadrootsSignerError> {
- let sig = "f".repeat(128);
- let raw_json = serde_json::json!({
- "id": draft.expected_event_id,
- "pubkey": self.pubkey().as_str(),
- "created_at": draft.created_at,
- "kind": draft.kind,
- "tags": draft.tags,
- "content": draft.content,
- "sig": sig,
- })
- .to_string();
- RadrootsSignedNostrEvent::new(RadrootsSignedNostrEventParts {
- id: draft.expected_event_id.clone(),
- pubkey: self.pubkey().as_str().to_owned(),
- created_at: draft.created_at,
- kind: draft.kind,
- tags: draft.tags.clone(),
- content: draft.content.clone(),
- sig,
- raw_json,
- })
- .map_err(|error| RadrootsSignerError::SigningFailed {
- message: error.to_string(),
- })
- }
-}
-
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let keys = RadrootsNostrKeys::generate();
+ let seller = keys.public_key().to_hex();
+ let signer = RadrootsSdkLocalKeySigner::new(keys)?;
let sdk = RadrootsSdk::builder()
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
+ .signer_provider(RadrootsSdkSignerProvider::LocalKey(signer))
.build()
.await?;
- let actor = RadrootsActorContext::test(SELLER, [RadrootsActorRole::Seller])?;
- let listing = sample_listing();
+ 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 idempotency_key = SdkIdempotencyKey::new("example-1")?;
@@ -92,7 +42,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
prepared.clone(),
target_relays,
Some(idempotency_key),
- &FixtureSigner::new(SELLER),
)
.await?;
let push = sdk
@@ -122,12 +71,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
-fn sample_listing() -> RadrootsListing {
+fn sample_listing(seller: &str) -> RadrootsListing {
RadrootsListing {
d_tag: RadrootsDTag::parse("AAAAAAAAAAAAAAAAAAAAAQ").expect("d tag"),
published_at: None,
farm: RadrootsFarmRef {
- pubkey: SELLER.to_owned(),
+ pubkey: seller.to_owned(),
d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_owned(),
},
product: RadrootsListingProduct {
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
@@ -1,4 +1,4 @@
-use radroots_authority::{RadrootsActorContext, RadrootsLocalEventSigner};
+use radroots_authority::RadrootsActorContext;
use radroots_core::{
RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
RadrootsCoreQuantityPrice, RadrootsCoreUnit,
@@ -13,8 +13,8 @@ use radroots_sdk::protocol::listing::{
};
use radroots_sdk::{
ListingPreparePublishRequest, OrderStatusRequest, PushOutboxRequest, RadrootsSdk,
- RadrootsSdkTimestamp, SdkIdempotencyKey, SdkRelayTargetPolicy, SdkRelayTargetSet,
- SdkRelayUrlPolicy,
+ RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SdkIdempotencyKey,
+ SdkRelayTargetPolicy, SdkRelayTargetSet, SdkRelayUrlPolicy,
};
const LOCAL_RELAY: &str = "ws://localhost:7777";
@@ -23,9 +23,10 @@ const LOCAL_RELAY: &str = "ws://localhost:7777";
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keys = RadrootsNostrKeys::generate();
let seller = keys.public_key().to_hex();
- let signer = RadrootsLocalEventSigner::new(keys)?;
+ let signer = RadrootsSdkLocalKeySigner::new(keys)?;
let sdk = RadrootsSdk::builder()
.fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
+ .signer_provider(RadrootsSdkSignerProvider::LocalKey(signer))
.build()
.await?;
let actor = RadrootsActorContext::test(seller.as_str(), [RadrootsActorRole::Seller])?;
@@ -45,7 +46,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
prepared,
target_policy,
Some(SdkIdempotencyKey::new("sdk-v1-local-example")?),
- &signer,
)
.await?;
let adapter = RadrootsMockRelayPublishAdapter::new();
diff --git a/crates/sdk/examples/sdk_v1_myc_nip46_signer_setup.rs b/crates/sdk/examples/sdk_v1_myc_nip46_signer_setup.rs
@@ -0,0 +1,56 @@
+use radroots_nostr::prelude::{RadrootsNostrEvent, RadrootsNostrKeys};
+use radroots_nostr_connect::prelude::{
+ RadrootsNostrConnectClientTarget, RadrootsNostrConnectError,
+};
+use radroots_sdk::{
+ RadrootsSdk, RadrootsSdkMycNip46Signer, RadrootsSdkNip46Transport,
+ RadrootsSdkNip46TransportFuture, RadrootsSdkSignerMode, RadrootsSdkSignerProvider,
+ radroots_sdk_myc_nip46_product_permission_strings,
+};
+use std::sync::Arc;
+
+struct ExampleNip46Transport;
+
+impl RadrootsSdkNip46Transport for ExampleNip46Transport {
+ fn publish_request_event<'a>(
+ &'a self,
+ _event: RadrootsNostrEvent,
+ ) -> RadrootsSdkNip46TransportFuture<'a, ()> {
+ Box::pin(async { Ok(()) })
+ }
+
+ fn next_response_event<'a>(
+ &'a self,
+ ) -> RadrootsSdkNip46TransportFuture<'a, RadrootsNostrEvent> {
+ Box::pin(async { Err(RadrootsNostrConnectError::RequestTimedOut) })
+ }
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let client_keys = RadrootsNostrKeys::generate();
+ let remote_signer_keys = RadrootsNostrKeys::generate();
+ let user_keys = RadrootsNostrKeys::generate();
+ let target = RadrootsNostrConnectClientTarget::new(
+ remote_signer_keys.public_key(),
+ vec![nostr::RelayUrl::parse("wss://relay.example.com")?],
+ );
+ let signer = RadrootsSdkMycNip46Signer::new(
+ client_keys,
+ target,
+ user_keys.public_key().to_hex(),
+ Arc::new(ExampleNip46Transport),
+ )?;
+ let sdk = RadrootsSdk::builder()
+ .signer_provider(RadrootsSdkSignerProvider::MycNip46(signer))
+ .build()
+ .await?;
+ let status = sdk.signer_status().expect("configured signer status");
+ let permissions = radroots_sdk_myc_nip46_product_permission_strings();
+
+ assert_eq!(status.mode, RadrootsSdkSignerMode::MycNip46);
+ assert!(permissions.iter().any(|value| value == "sign_event:30340"));
+ println!("configured signer mode: {}", status.mode.as_str());
+ println!("requested permissions: {}", permissions.join(","));
+ Ok(())
+}
diff --git a/crates/sdk/src/farms_runtime.rs b/crates/sdk/src/farms_runtime.rs
@@ -1,3 +1,5 @@
+#[cfg(feature = "signer-adapters")]
+use crate::workflow_runtime::enqueue_configured_signed_workflow;
#[cfg(feature = "runtime")]
use crate::{
FarmsClient, RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkMutationState,
@@ -140,10 +142,10 @@ impl<'sdk> FarmsClient<'sdk> {
farm_publish_plan(&request.actor, request.farm, created_at)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_publish(
&self,
request: FarmEnqueuePublishRequest,
- signer: &dyn RadrootsEventSigner,
) -> Result<FarmEnqueueReceipt, RadrootsSdkError> {
let FarmEnqueuePublishRequest {
actor,
@@ -158,16 +160,66 @@ impl<'sdk> FarmsClient<'sdk> {
created_at,
};
let plan = self.prepare_publish(prepare_request)?;
- self.enqueue_prepared_publish(&actor, plan, target_relays, idempotency_key, signer)
+ self.enqueue_prepared_publish(&actor, plan, target_relays, idempotency_key)
.await
}
+ pub async fn enqueue_publish_with_explicit_signer(
+ &self,
+ request: FarmEnqueuePublishRequest,
+ signer: &dyn RadrootsEventSigner,
+ ) -> Result<FarmEnqueueReceipt, RadrootsSdkError> {
+ let FarmEnqueuePublishRequest {
+ actor,
+ farm,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = FarmPreparePublishRequest {
+ actor: actor.clone(),
+ farm,
+ created_at,
+ };
+ let plan = self.prepare_publish(prepare_request)?;
+ self.enqueue_prepared_publish_with_explicit_signer(
+ &actor,
+ plan,
+ target_relays,
+ idempotency_key,
+ signer,
+ )
+ .await
+ }
+
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_publish(
&self,
actor: &RadrootsActorContext,
plan: FarmPublishPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<FarmEnqueueReceipt, RadrootsSdkError> {
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: FARM_PUBLISH_OPERATION_KIND,
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(farm_enqueue_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_publish_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: FarmPublishPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<FarmEnqueueReceipt, RadrootsSdkError> {
let enqueue = enqueue_signed_workflow(
@@ -182,16 +234,7 @@ impl<'sdk> FarmsClient<'sdk> {
signer,
)
.await?;
- Ok(FarmEnqueueReceipt {
- farm_addr: plan.farm_addr,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(farm_enqueue_receipt(plan, enqueue))
}
fn resolved_created_at(
@@ -206,6 +249,23 @@ impl<'sdk> FarmsClient<'sdk> {
}
#[cfg(feature = "runtime")]
+fn farm_enqueue_receipt(
+ plan: FarmPublishPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> FarmEnqueueReceipt {
+ FarmEnqueueReceipt {
+ farm_addr: plan.farm_addr,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
fn farm_publish_plan(
actor: &RadrootsActorContext,
farm_value: RadrootsFarm,
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
@@ -96,11 +96,12 @@ pub use crate::runtime::{
};
#[cfg(all(feature = "runtime", feature = "signer-adapters"))]
pub use crate::signer_provider::{
- RadrootsSdkLocalKeySigner, RadrootsSdkMycNip46Signer, RadrootsSdkNip46Transport,
- RadrootsSdkNip46TransportFuture, RadrootsSdkSignReceipt, RadrootsSdkSignRequest,
- RadrootsSdkSignerCapability, RadrootsSdkSignerMode, RadrootsSdkSignerProgress,
- RadrootsSdkSignerProgressSink, RadrootsSdkSignerProvider, RadrootsSdkSignerState,
- RadrootsSdkSignerStatus,
+ RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS, RadrootsSdkLocalKeySigner,
+ RadrootsSdkMycNip46Signer, RadrootsSdkNip46Transport, RadrootsSdkNip46TransportFuture,
+ RadrootsSdkSignReceipt, RadrootsSdkSignRequest, RadrootsSdkSignerCapability,
+ RadrootsSdkSignerMode, RadrootsSdkSignerProgress, RadrootsSdkSignerProgressSink,
+ RadrootsSdkSignerProvider, RadrootsSdkSignerState, RadrootsSdkSignerStatus,
+ radroots_sdk_myc_nip46_product_permission_strings, radroots_sdk_myc_nip46_product_permissions,
};
#[cfg(feature = "runtime")]
pub use crate::sync_runtime::{
diff --git a/crates/sdk/src/listings_runtime.rs b/crates/sdk/src/listings_runtime.rs
@@ -1,3 +1,5 @@
+#[cfg(feature = "signer-adapters")]
+use crate::workflow_runtime::enqueue_configured_signed_workflow;
#[cfg(feature = "runtime")]
use crate::{
ListingsClient, RadrootsSdkError, RadrootsSdkTimestamp, SdkIdempotencyKey,
@@ -184,10 +186,10 @@ impl<'sdk> ListingsClient<'sdk> {
listing_publish_plan(&request.actor, request.document, created_at)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_publish(
&self,
request: ListingEnqueuePublishRequest,
- signer: &dyn RadrootsEventSigner,
) -> Result<ListingEnqueueReceipt, RadrootsSdkError> {
let ListingEnqueuePublishRequest {
actor,
@@ -202,16 +204,66 @@ impl<'sdk> ListingsClient<'sdk> {
created_at,
};
let plan = self.prepare_publish(prepare_request)?;
- self.enqueue_prepared_publish(&actor, plan, target_relays, idempotency_key, signer)
+ self.enqueue_prepared_publish(&actor, plan, target_relays, idempotency_key)
.await
}
+ pub async fn enqueue_publish_with_explicit_signer(
+ &self,
+ request: ListingEnqueuePublishRequest,
+ signer: &dyn RadrootsEventSigner,
+ ) -> Result<ListingEnqueueReceipt, RadrootsSdkError> {
+ let ListingEnqueuePublishRequest {
+ actor,
+ document,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = ListingPreparePublishRequest {
+ actor: actor.clone(),
+ document,
+ created_at,
+ };
+ let plan = self.prepare_publish(prepare_request)?;
+ self.enqueue_prepared_publish_with_explicit_signer(
+ &actor,
+ plan,
+ target_relays,
+ idempotency_key,
+ signer,
+ )
+ .await
+ }
+
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_publish(
&self,
actor: &RadrootsActorContext,
plan: ListingPublishPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<ListingEnqueueReceipt, RadrootsSdkError> {
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: LISTING_PUBLISH_OPERATION_KIND,
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(listing_enqueue_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_publish_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: ListingPublishPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<ListingEnqueueReceipt, RadrootsSdkError> {
let enqueue = enqueue_signed_workflow(
@@ -226,17 +278,7 @@ impl<'sdk> ListingsClient<'sdk> {
signer,
)
.await?;
- Ok(ListingEnqueueReceipt {
- public_listing_addr: plan.public_listing_addr,
- draft_listing_addr: plan.draft_listing_addr,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(listing_enqueue_receipt(plan, enqueue))
}
fn resolved_created_at(
@@ -251,6 +293,24 @@ impl<'sdk> ListingsClient<'sdk> {
}
#[cfg(feature = "runtime")]
+fn listing_enqueue_receipt(
+ plan: ListingPublishPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> ListingEnqueueReceipt {
+ ListingEnqueueReceipt {
+ public_listing_addr: plan.public_listing_addr,
+ draft_listing_addr: plan.draft_listing_addr,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
fn canonical_listing_draft(
actor: &RadrootsActorContext,
document: RadrootsListingDraftDocumentV1,
diff --git a/crates/sdk/src/orders_runtime.rs b/crates/sdk/src/orders_runtime.rs
@@ -1,3 +1,5 @@
+#[cfg(feature = "signer-adapters")]
+use crate::workflow_runtime::enqueue_configured_signed_workflow;
#[cfg(feature = "runtime")]
use crate::{
OrdersClient, RadrootsSdkError, RadrootsSdkRecoveryAction, RadrootsSdkTimestamp,
@@ -1159,10 +1161,10 @@ impl<'sdk> OrdersClient<'sdk> {
)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_submit(
&self,
request: OrderSubmitEnqueueRequest,
- signer: &dyn RadrootsEventSigner,
) -> Result<OrderSubmitReceipt, RadrootsSdkError> {
let OrderSubmitEnqueueRequest {
actor,
@@ -1179,16 +1181,68 @@ impl<'sdk> OrdersClient<'sdk> {
created_at,
};
let plan = self.prepare_submit(prepare_request)?;
- self.enqueue_prepared_submit(&actor, plan, target_relays, idempotency_key, signer)
+ self.enqueue_prepared_submit(&actor, plan, target_relays, idempotency_key)
.await
}
+ pub async fn enqueue_submit_with_explicit_signer(
+ &self,
+ request: OrderSubmitEnqueueRequest,
+ signer: &dyn RadrootsEventSigner,
+ ) -> Result<OrderSubmitReceipt, RadrootsSdkError> {
+ let OrderSubmitEnqueueRequest {
+ actor,
+ listing_event,
+ order,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = OrderSubmitPrepareRequest {
+ actor: actor.clone(),
+ listing_event,
+ order,
+ created_at,
+ };
+ let plan = self.prepare_submit(prepare_request)?;
+ self.enqueue_prepared_submit_with_explicit_signer(
+ &actor,
+ plan,
+ target_relays,
+ idempotency_key,
+ signer,
+ )
+ .await
+ }
+
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_submit(
&self,
actor: &RadrootsActorContext,
plan: OrderSubmitPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<OrderSubmitReceipt, RadrootsSdkError> {
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: OrderWorkflowKind::Submit.operation_kind(),
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(order_submit_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_submit_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: OrderSubmitPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderSubmitReceipt, RadrootsSdkError> {
let enqueue = enqueue_signed_workflow(
@@ -1203,23 +1257,7 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderSubmitReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::Submit,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- listing_event_id: plan.listing_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(order_submit_receipt(plan, enqueue))
}
pub fn prepare_decision(
@@ -1235,10 +1273,10 @@ impl<'sdk> OrdersClient<'sdk> {
)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_decision(
&self,
request: OrderDecisionEnqueueRequest,
- signer: &dyn RadrootsEventSigner,
) -> Result<OrderDecisionReceipt, RadrootsSdkError> {
let OrderDecisionEnqueueRequest {
actor,
@@ -1255,16 +1293,74 @@ impl<'sdk> OrdersClient<'sdk> {
created_at,
};
let plan = self.prepare_decision(prepare_request)?;
- self.enqueue_prepared_decision(&actor, plan, target_relays, idempotency_key, signer)
+ self.enqueue_prepared_decision(&actor, plan, target_relays, idempotency_key)
.await
}
+ pub async fn enqueue_decision_with_explicit_signer(
+ &self,
+ request: OrderDecisionEnqueueRequest,
+ signer: &dyn RadrootsEventSigner,
+ ) -> Result<OrderDecisionReceipt, RadrootsSdkError> {
+ let OrderDecisionEnqueueRequest {
+ actor,
+ request_event,
+ decision,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = OrderDecisionPrepareRequest {
+ actor: actor.clone(),
+ request_event,
+ decision,
+ created_at,
+ };
+ let plan = self.prepare_decision(prepare_request)?;
+ self.enqueue_prepared_decision_with_explicit_signer(
+ &actor,
+ plan,
+ target_relays,
+ idempotency_key,
+ signer,
+ )
+ .await
+ }
+
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_decision(
&self,
actor: &RadrootsActorContext,
plan: OrderDecisionPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<OrderDecisionReceipt, RadrootsSdkError> {
+ if !self
+ .prepared_order_event_exists(&plan.expected_event_id)
+ .await?
+ {
+ self.require_decision_preflight(&plan).await?;
+ }
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: OrderWorkflowKind::Decision.operation_kind(),
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(order_decision_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_decision_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: OrderDecisionPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderDecisionReceipt, RadrootsSdkError> {
if !self
@@ -1285,25 +1381,7 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderDecisionReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::Decision,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- buyer_pubkey: plan.buyer_pubkey,
- seller_pubkey: plan.seller_pubkey,
- request_event_id: plan.request_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(order_decision_receipt(plan, enqueue))
}
pub fn prepare_revision_proposal(
@@ -1320,9 +1398,35 @@ impl<'sdk> OrdersClient<'sdk> {
)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_revision_proposal(
&self,
request: OrderRevisionProposalEnqueueRequest,
+ ) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError> {
+ let OrderRevisionProposalEnqueueRequest {
+ actor,
+ root_event,
+ previous_event,
+ proposal,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = OrderRevisionProposalPrepareRequest {
+ actor: actor.clone(),
+ root_event,
+ previous_event,
+ proposal,
+ created_at,
+ };
+ let plan = self.prepare_revision_proposal(prepare_request)?;
+ self.enqueue_prepared_revision_proposal(&actor, plan, target_relays, idempotency_key)
+ .await
+ }
+
+ pub async fn enqueue_revision_proposal_with_explicit_signer(
+ &self,
+ request: OrderRevisionProposalEnqueueRequest,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError> {
let OrderRevisionProposalEnqueueRequest {
@@ -1342,7 +1446,7 @@ impl<'sdk> OrdersClient<'sdk> {
created_at,
};
let plan = self.prepare_revision_proposal(prepare_request)?;
- self.enqueue_prepared_revision_proposal(
+ self.enqueue_prepared_revision_proposal_with_explicit_signer(
&actor,
plan,
target_relays,
@@ -1352,12 +1456,40 @@ impl<'sdk> OrdersClient<'sdk> {
.await
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_revision_proposal(
&self,
actor: &RadrootsActorContext,
plan: OrderRevisionProposalPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError> {
+ if !self
+ .prepared_order_event_exists(&plan.expected_event_id)
+ .await?
+ {
+ self.require_revision_proposal_preflight(&plan).await?;
+ }
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: OrderWorkflowKind::RevisionProposal.operation_kind(),
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(order_revision_proposal_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_revision_proposal_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: OrderRevisionProposalPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError> {
if !self
@@ -1378,26 +1510,7 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderRevisionProposalReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::RevisionProposal,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- buyer_pubkey: plan.buyer_pubkey,
- seller_pubkey: plan.seller_pubkey,
- root_event_id: plan.root_event_id,
- previous_event_id: plan.previous_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(order_revision_proposal_receipt(plan, enqueue))
}
pub fn prepare_revision_decision(
@@ -1414,9 +1527,35 @@ impl<'sdk> OrdersClient<'sdk> {
)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_revision_decision(
&self,
request: OrderRevisionDecisionEnqueueRequest,
+ ) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError> {
+ let OrderRevisionDecisionEnqueueRequest {
+ actor,
+ root_event,
+ previous_event,
+ decision,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = OrderRevisionDecisionPrepareRequest {
+ actor: actor.clone(),
+ root_event,
+ previous_event,
+ decision,
+ created_at,
+ };
+ let plan = self.prepare_revision_decision(prepare_request)?;
+ self.enqueue_prepared_revision_decision(&actor, plan, target_relays, idempotency_key)
+ .await
+ }
+
+ pub async fn enqueue_revision_decision_with_explicit_signer(
+ &self,
+ request: OrderRevisionDecisionEnqueueRequest,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError> {
let OrderRevisionDecisionEnqueueRequest {
@@ -1436,7 +1575,7 @@ impl<'sdk> OrdersClient<'sdk> {
created_at,
};
let plan = self.prepare_revision_decision(prepare_request)?;
- self.enqueue_prepared_revision_decision(
+ self.enqueue_prepared_revision_decision_with_explicit_signer(
&actor,
plan,
target_relays,
@@ -1446,12 +1585,40 @@ impl<'sdk> OrdersClient<'sdk> {
.await
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_revision_decision(
&self,
actor: &RadrootsActorContext,
plan: OrderRevisionDecisionPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError> {
+ if !self
+ .prepared_order_event_exists(&plan.expected_event_id)
+ .await?
+ {
+ self.require_revision_decision_preflight(&plan).await?;
+ }
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: OrderWorkflowKind::RevisionDecision.operation_kind(),
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(order_revision_decision_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_revision_decision_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: OrderRevisionDecisionPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError> {
if !self
@@ -1472,26 +1639,7 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderRevisionDecisionReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::RevisionDecision,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- buyer_pubkey: plan.buyer_pubkey,
- seller_pubkey: plan.seller_pubkey,
- root_event_id: plan.root_event_id,
- previous_event_id: plan.previous_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(order_revision_decision_receipt(plan, enqueue))
}
pub fn prepare_cancellation(
@@ -1508,10 +1656,10 @@ impl<'sdk> OrdersClient<'sdk> {
)
}
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_cancellation(
&self,
request: OrderCancellationEnqueueRequest,
- signer: &dyn RadrootsEventSigner,
) -> Result<OrderCancellationReceipt, RadrootsSdkError> {
let OrderCancellationEnqueueRequest {
actor,
@@ -1530,16 +1678,76 @@ impl<'sdk> OrdersClient<'sdk> {
created_at,
};
let plan = self.prepare_cancellation(prepare_request)?;
- self.enqueue_prepared_cancellation(&actor, plan, target_relays, idempotency_key, signer)
+ self.enqueue_prepared_cancellation(&actor, plan, target_relays, idempotency_key)
.await
}
+ pub async fn enqueue_cancellation_with_explicit_signer(
+ &self,
+ request: OrderCancellationEnqueueRequest,
+ signer: &dyn RadrootsEventSigner,
+ ) -> Result<OrderCancellationReceipt, RadrootsSdkError> {
+ let OrderCancellationEnqueueRequest {
+ actor,
+ root_event,
+ previous_event,
+ cancellation,
+ target_relays,
+ idempotency_key,
+ created_at,
+ } = request;
+ let prepare_request = OrderCancellationPrepareRequest {
+ actor: actor.clone(),
+ root_event,
+ previous_event,
+ cancellation,
+ created_at,
+ };
+ let plan = self.prepare_cancellation(prepare_request)?;
+ self.enqueue_prepared_cancellation_with_explicit_signer(
+ &actor,
+ plan,
+ target_relays,
+ idempotency_key,
+ signer,
+ )
+ .await
+ }
+
+ #[cfg(feature = "signer-adapters")]
pub async fn enqueue_prepared_cancellation(
&self,
actor: &RadrootsActorContext,
plan: OrderCancellationPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
+ ) -> Result<OrderCancellationReceipt, RadrootsSdkError> {
+ if !self
+ .prepared_order_event_exists(&plan.expected_event_id)
+ .await?
+ {
+ self.require_cancellation_preflight(&plan).await?;
+ }
+ let enqueue = enqueue_configured_signed_workflow(
+ self.sdk,
+ SdkWorkflowEnqueueRequest {
+ operation_kind: OrderWorkflowKind::Cancellation.operation_kind(),
+ actor,
+ frozen_draft: &plan.frozen_draft,
+ target_relays,
+ idempotency_key,
+ },
+ )
+ .await?;
+ Ok(order_cancellation_receipt(plan, enqueue))
+ }
+
+ pub async fn enqueue_prepared_cancellation_with_explicit_signer(
+ &self,
+ actor: &RadrootsActorContext,
+ plan: OrderCancellationPlan,
+ target_relays: SdkRelayTargetPolicy,
+ idempotency_key: Option<SdkIdempotencyKey>,
signer: &dyn RadrootsEventSigner,
) -> Result<OrderCancellationReceipt, RadrootsSdkError> {
if !self
@@ -1560,26 +1768,7 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderCancellationReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::Cancellation,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- buyer_pubkey: plan.buyer_pubkey,
- seller_pubkey: plan.seller_pubkey,
- root_event_id: plan.root_event_id,
- previous_event_id: plan.previous_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
+ Ok(order_cancellation_receipt(plan, enqueue))
}
pub async fn status(
@@ -2124,6 +2313,137 @@ fn order_workflow_plan(
}
#[cfg(feature = "runtime")]
+fn order_submit_receipt(
+ plan: OrderSubmitPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> OrderSubmitReceipt {
+ OrderSubmitReceipt {
+ workflow: order_workflow_enqueue_receipt(
+ OrderWorkflowKind::Submit,
+ plan.expected_event_id.clone(),
+ &enqueue,
+ ),
+ order_id: plan.order_id,
+ listing_addr: plan.listing_addr,
+ listing_event_id: plan.listing_event_id,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
+fn order_decision_receipt(
+ plan: OrderDecisionPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> OrderDecisionReceipt {
+ OrderDecisionReceipt {
+ workflow: order_workflow_enqueue_receipt(
+ OrderWorkflowKind::Decision,
+ plan.expected_event_id.clone(),
+ &enqueue,
+ ),
+ order_id: plan.order_id,
+ listing_addr: plan.listing_addr,
+ buyer_pubkey: plan.buyer_pubkey,
+ seller_pubkey: plan.seller_pubkey,
+ request_event_id: plan.request_event_id,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
+fn order_revision_proposal_receipt(
+ plan: OrderRevisionProposalPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> OrderRevisionProposalReceipt {
+ OrderRevisionProposalReceipt {
+ workflow: order_workflow_enqueue_receipt(
+ OrderWorkflowKind::RevisionProposal,
+ plan.expected_event_id.clone(),
+ &enqueue,
+ ),
+ order_id: plan.order_id,
+ listing_addr: plan.listing_addr,
+ buyer_pubkey: plan.buyer_pubkey,
+ seller_pubkey: plan.seller_pubkey,
+ root_event_id: plan.root_event_id,
+ previous_event_id: plan.previous_event_id,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
+fn order_revision_decision_receipt(
+ plan: OrderRevisionDecisionPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> OrderRevisionDecisionReceipt {
+ OrderRevisionDecisionReceipt {
+ workflow: order_workflow_enqueue_receipt(
+ OrderWorkflowKind::RevisionDecision,
+ plan.expected_event_id.clone(),
+ &enqueue,
+ ),
+ order_id: plan.order_id,
+ listing_addr: plan.listing_addr,
+ buyer_pubkey: plan.buyer_pubkey,
+ seller_pubkey: plan.seller_pubkey,
+ root_event_id: plan.root_event_id,
+ previous_event_id: plan.previous_event_id,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
+fn order_cancellation_receipt(
+ plan: OrderCancellationPlan,
+ enqueue: crate::workflow_runtime::SdkWorkflowEnqueueReceipt,
+) -> OrderCancellationReceipt {
+ OrderCancellationReceipt {
+ workflow: order_workflow_enqueue_receipt(
+ OrderWorkflowKind::Cancellation,
+ plan.expected_event_id.clone(),
+ &enqueue,
+ ),
+ order_id: plan.order_id,
+ listing_addr: plan.listing_addr,
+ buyer_pubkey: plan.buyer_pubkey,
+ seller_pubkey: plan.seller_pubkey,
+ root_event_id: plan.root_event_id,
+ previous_event_id: plan.previous_event_id,
+ expected_event_id: plan.expected_event_id,
+ signed_event_id: enqueue.signed_event_id,
+ local_event_seq: enqueue.local_event_seq,
+ outbox_operation_id: enqueue.outbox_operation_id,
+ outbox_event_id: enqueue.outbox_event_id,
+ state: enqueue.state.into(),
+ idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
+ }
+}
+
+#[cfg(feature = "runtime")]
fn order_workflow_enqueue_receipt(
kind: OrderWorkflowKind,
expected_event_id: RadrootsEventId,
diff --git a/crates/sdk/src/signer_provider.rs b/crates/sdk/src/signer_provider.rs
@@ -7,11 +7,16 @@ use radroots_authority::{
};
use radroots_events::draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent};
use radroots_events::ids::RadrootsPublicKey;
+use radroots_events::kinds::{
+ KIND_FARM, KIND_LISTING, KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_REQUEST,
+ KIND_ORDER_REVISION_DECISION, KIND_ORDER_REVISION_PROPOSAL,
+};
use radroots_nostr::prelude::{RadrootsNostrEvent, RadrootsNostrKeys, radroots_event_from_nostr};
use radroots_nostr_connect::prelude::{
RadrootsNostrConnectClientRequest, RadrootsNostrConnectClientTarget,
RadrootsNostrConnectClientTransport, RadrootsNostrConnectClientTransportFuture,
- RadrootsNostrConnectError, RadrootsNostrConnectRequest, RadrootsNostrConnectResponse,
+ RadrootsNostrConnectError, RadrootsNostrConnectMethod, RadrootsNostrConnectPermission,
+ RadrootsNostrConnectPermissions, RadrootsNostrConnectRequest, RadrootsNostrConnectResponse,
execute_request_with_transport,
};
use serde_json::json;
@@ -22,6 +27,16 @@ use std::sync::{
pub type RadrootsSdkNip46TransportFuture<'a, T> = RadrootsNostrConnectClientTransportFuture<'a, T>;
+pub const RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS: [u32; 7] = [
+ KIND_FARM,
+ KIND_LISTING,
+ KIND_ORDER_REQUEST,
+ KIND_ORDER_DECISION,
+ KIND_ORDER_REVISION_PROPOSAL,
+ KIND_ORDER_REVISION_DECISION,
+ KIND_ORDER_CANCELLATION,
+];
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
@@ -64,6 +79,7 @@ pub struct RadrootsSdkSignerCapability {
pub remote_signer_pubkey: Option<String>,
pub relays: Vec<String>,
pub can_sign_events: bool,
+ pub nip46_permissions: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
@@ -229,6 +245,7 @@ impl RadrootsSdkLocalKeySigner {
remote_signer_pubkey: None,
relays: Vec::new(),
can_sign_events: true,
+ nip46_permissions: Vec::new(),
}
}
@@ -311,6 +328,7 @@ impl RadrootsSdkMycNip46Signer {
remote_signer_pubkey: Some(self.target.remote_signer_public_key.to_hex()),
relays: self.target.relays.iter().map(ToString::to_string).collect(),
can_sign_events: true,
+ nip46_permissions: radroots_sdk_myc_nip46_product_permission_strings(),
}
}
@@ -388,6 +406,27 @@ impl RadrootsSdkMycNip46Signer {
}
}
+pub fn radroots_sdk_myc_nip46_product_permissions() -> RadrootsNostrConnectPermissions {
+ RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS
+ .iter()
+ .map(|kind| {
+ RadrootsNostrConnectPermission::with_parameter(
+ RadrootsNostrConnectMethod::SignEvent,
+ kind.to_string(),
+ )
+ })
+ .collect::<Vec<_>>()
+ .into()
+}
+
+pub fn radroots_sdk_myc_nip46_product_permission_strings() -> Vec<String> {
+ radroots_sdk_myc_nip46_product_permissions()
+ .as_slice()
+ .iter()
+ .map(ToString::to_string)
+ .collect()
+}
+
struct RadrootsSdkSignerIdentityOnly {
pubkey: RadrootsPublicKey,
}
diff --git a/crates/sdk/src/workflow_runtime.rs b/crates/sdk/src/workflow_runtime.rs
@@ -1,3 +1,5 @@
+#[cfg(feature = "signer-adapters")]
+use crate::RadrootsSdkSignRequest;
use crate::{
RadrootsSdk, RadrootsSdkError, SdkIdempotencyKey, SdkRelayTargetPolicy, SdkRelayTargetSet,
runtime::sdk_now_ms,
@@ -36,6 +38,32 @@ pub(crate) async fn enqueue_signed_workflow(
) -> Result<SdkWorkflowEnqueueReceipt, RadrootsSdkError> {
let target_relays = resolved_target_relays(sdk, &request.target_relays)?;
let signed_event = sign_authorized_draft(request.actor, signer, request.frozen_draft)?;
+ enqueue_signed_workflow_event(sdk, request, signed_event, target_relays).await
+}
+
+#[cfg(feature = "signer-adapters")]
+pub(crate) async fn enqueue_configured_signed_workflow(
+ sdk: &RadrootsSdk,
+ request: SdkWorkflowEnqueueRequest<'_>,
+) -> Result<SdkWorkflowEnqueueReceipt, RadrootsSdkError> {
+ let target_relays = resolved_target_relays(sdk, &request.target_relays)?;
+ let signed_event = sdk
+ .sign_with_configured_signer(RadrootsSdkSignRequest::new(
+ request.operation_kind,
+ request.actor,
+ request.frozen_draft,
+ ))
+ .await?
+ .signed_event;
+ enqueue_signed_workflow_event(sdk, request, signed_event, target_relays).await
+}
+
+async fn enqueue_signed_workflow_event(
+ sdk: &RadrootsSdk,
+ request: SdkWorkflowEnqueueRequest<'_>,
+ signed_event: RadrootsSignedNostrEvent,
+ target_relays: SdkResolvedRelayTargets,
+) -> Result<SdkWorkflowEnqueueReceipt, RadrootsSdkError> {
let idempotency_key = match request.idempotency_key {
Some(idempotency_key) => idempotency_key,
None => SdkIdempotencyKey::derive(
diff --git a/crates/sdk/tests/farms_runtime.rs b/crates/sdk/tests/farms_runtime.rs
@@ -208,7 +208,7 @@ async fn farm_enqueue_publish_stores_event_and_queues_signed_outbox_without_prof
.expect("prepared");
let receipt = sdk
.farms()
- .enqueue_publish(request, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(FARMER))
.await
.expect("enqueue");
@@ -265,7 +265,7 @@ async fn farm_enqueue_publish_returns_sanitized_signer_errors_before_mutation()
);
let error = sdk
.farms()
- .enqueue_publish(request, &FixtureSigner::new(OTHER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(OTHER))
.await
.expect_err("signer error");
let message = error.to_string();
@@ -322,12 +322,12 @@ async fn farm_enqueue_publish_derives_order_independent_idempotency_key() {
let first_receipt = sdk
.farms()
- .enqueue_publish(first, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(first, &FixtureSigner::new(FARMER))
.await
.expect("first enqueue");
let second_receipt = sdk
.farms()
- .enqueue_publish(second, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(second, &FixtureSigner::new(FARMER))
.await
.expect("second enqueue");
@@ -367,7 +367,7 @@ async fn farm_enqueue_publish_pushes_queued_event_with_mock_relay_sync() {
.expect("target relays");
let enqueue_receipt = sdk
.farms()
- .enqueue_publish(enqueue_request, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(enqueue_request, &FixtureSigner::new(FARMER))
.await
.expect("enqueue");
let adapter = RadrootsMockRelayPublishAdapter::new();
@@ -423,7 +423,7 @@ async fn farm_enqueue_publish_reports_partial_local_mutation_after_outbox_confli
.try_with_idempotency_key("farm-idem-e")
.expect("idempotency key");
sdk.farms()
- .enqueue_publish(first, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(first, &FixtureSigner::new(FARMER))
.await
.expect("first enqueue");
@@ -436,7 +436,7 @@ async fn farm_enqueue_publish_reports_partial_local_mutation_after_outbox_confli
.expect("idempotency key");
let error = sdk
.farms()
- .enqueue_publish(second, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(second, &FixtureSigner::new(FARMER))
.await
.expect_err("partial");
@@ -549,7 +549,7 @@ async fn farm_runtime_dtos_serialize_deterministically() {
let receipt = sdk
.farms()
- .enqueue_publish(enqueue_request, &FixtureSigner::new(FARMER))
+ .enqueue_publish_with_explicit_signer(enqueue_request, &FixtureSigner::new(FARMER))
.await
.expect("enqueue");
let receipt_json = serde_json::to_value(&receipt).expect("receipt json");
diff --git a/crates/sdk/tests/listings_runtime.rs b/crates/sdk/tests/listings_runtime.rs
@@ -251,7 +251,7 @@ async fn enqueue_publish_stores_event_and_queues_signed_outbox_without_publish()
.expect("prepared");
let receipt = sdk
.listings()
- .enqueue_publish(request, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(SELLER))
.await
.expect("enqueue");
@@ -300,7 +300,7 @@ async fn enqueue_publish_use_configured_relays_rejects_empty_builder_relays() {
let error = sdk
.listings()
- .enqueue_publish(request, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(SELLER))
.await
.expect_err("empty configured relays");
@@ -324,7 +324,7 @@ async fn prepare_then_enqueue_prepared_uses_same_event_id() {
.expect("prepared");
let receipt = sdk
.listings()
- .enqueue_prepared_publish(
+ .enqueue_prepared_publish_with_explicit_signer(
&actor,
prepared.clone(),
SdkRelayTargetPolicy::UseConfiguredRelays,
@@ -372,7 +372,7 @@ async fn enqueue_receipt_debug_omits_signed_event_payload_material() {
.expect("idempotency key");
let receipt = sdk
.listings()
- .enqueue_publish(request, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(SELLER))
.await
.expect("enqueue");
let debug = format!("{receipt:?}");
@@ -463,7 +463,7 @@ async fn listing_runtime_dtos_serialize_deterministically() {
let receipt = sdk
.listings()
- .enqueue_publish(enqueue_request, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(enqueue_request, &FixtureSigner::new(SELLER))
.await
.expect("enqueue");
let receipt_json = serde_json::to_value(&receipt).expect("receipt json");
@@ -486,7 +486,7 @@ async fn enqueue_publish_convenience_matches_prepare_plus_enqueue_prepared() {
.expect("prepared plan");
let prepared_receipt = prepared_sdk
.listings()
- .enqueue_prepared_publish(
+ .enqueue_prepared_publish_with_explicit_signer(
&prepared_actor,
prepared_plan,
SdkRelayTargetPolicy::UseConfiguredRelays,
@@ -504,7 +504,7 @@ async fn enqueue_publish_convenience_matches_prepare_plus_enqueue_prepared() {
);
let convenience_receipt = convenience_sdk
.listings()
- .enqueue_publish(convenience_request, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(convenience_request, &FixtureSigner::new(SELLER))
.await
.expect("convenience enqueue");
@@ -523,7 +523,7 @@ async fn enqueue_prepared_publish_returns_structured_actor_errors() {
.expect("prepared");
let error = sdk
.listings()
- .enqueue_prepared_publish(
+ .enqueue_prepared_publish_with_explicit_signer(
&non_seller_actor(),
prepared,
SdkRelayTargetPolicy::UseConfiguredRelays,
@@ -549,7 +549,7 @@ async fn enqueue_prepared_publish_returns_sanitized_signer_errors() {
.expect("prepared");
let error = sdk
.listings()
- .enqueue_prepared_publish(
+ .enqueue_prepared_publish_with_explicit_signer(
&actor,
prepared,
SdkRelayTargetPolicy::UseConfiguredRelays,
@@ -582,7 +582,7 @@ async fn explicit_historical_created_at_does_not_backdate_observed_at_ms() {
let receipt = sdk
.listings()
- .enqueue_publish(request, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(SELLER))
.await
.expect("enqueue");
@@ -626,7 +626,7 @@ async fn enqueue_publish_returns_sanitized_signer_errors() {
);
let error = sdk
.listings()
- .enqueue_publish(request, &FixtureSigner::new(OTHER))
+ .enqueue_publish_with_explicit_signer(request, &FixtureSigner::new(OTHER))
.await
.expect_err("signer error");
let message = error.to_string();
@@ -650,7 +650,7 @@ async fn enqueue_publish_reports_partial_local_mutation_after_outbox_conflict()
.try_with_idempotency_key("idem-d")
.expect("idempotency key");
sdk.listings()
- .enqueue_publish(first, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(first, &FixtureSigner::new(SELLER))
.await
.expect("first enqueue");
@@ -663,7 +663,7 @@ async fn enqueue_publish_reports_partial_local_mutation_after_outbox_conflict()
.expect("idempotency key");
let error = sdk
.listings()
- .enqueue_publish(second, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(second, &FixtureSigner::new(SELLER))
.await
.expect_err("partial");
@@ -702,12 +702,12 @@ async fn enqueue_publish_derives_order_independent_idempotency_key() {
let first_receipt = sdk
.listings()
- .enqueue_publish(first, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(first, &FixtureSigner::new(SELLER))
.await
.expect("first enqueue");
let second_receipt = sdk
.listings()
- .enqueue_publish(second, &FixtureSigner::new(SELLER))
+ .enqueue_publish_with_explicit_signer(second, &FixtureSigner::new(SELLER))
.await
.expect("second enqueue");
diff --git a/crates/sdk/tests/orders_runtime.rs b/crates/sdk/tests/orders_runtime.rs
@@ -685,7 +685,7 @@ async fn order_submit_enqueue_stores_event_queues_outbox_and_status_sees_request
let receipt = sdk
.orders()
- .enqueue_submit(request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
.await
.expect("enqueue");
@@ -800,7 +800,7 @@ async fn order_submit_enqueue_returns_sanitized_signer_errors_before_mutation()
let error = sdk
.orders()
- .enqueue_submit(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
.await
.expect_err("signer error");
let message = error.to_string();
@@ -856,12 +856,12 @@ async fn order_submit_enqueue_derives_order_independent_idempotency_key() {
let first_receipt = sdk
.orders()
- .enqueue_submit(first, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(first, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
.await
.expect("first enqueue");
let second_receipt = sdk
.orders()
- .enqueue_submit(second, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(second, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
.await
.expect("second enqueue");
@@ -928,7 +928,10 @@ async fn order_submit_enqueue_pushes_queued_event_with_mock_relay_sync() {
.expect("target relays");
let enqueue_receipt = sdk
.orders()
- .enqueue_submit(enqueue_request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(
+ enqueue_request,
+ &FixtureSigner::new(BUYER_SECRET_KEY_HEX),
+ )
.await
.expect("enqueue");
let adapter = RadrootsMockRelayPublishAdapter::new();
@@ -987,7 +990,7 @@ async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_confli
.try_with_idempotency_key("order-submit-conflict-idempotency")
.expect("first idempotency key");
sdk.orders()
- .enqueue_submit(first, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(first, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
.await
.expect("first enqueue");
@@ -1003,7 +1006,7 @@ async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_confli
.expect("second idempotency key");
let error = sdk
.orders()
- .enqueue_submit(second, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(second, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
.await
.expect_err("partial");
@@ -1129,7 +1132,10 @@ async fn order_submit_runtime_dtos_serialize_deterministically() {
let receipt = sdk
.orders()
- .enqueue_submit(enqueue_request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_submit_with_explicit_signer(
+ enqueue_request,
+ &FixtureSigner::new(BUYER_SECRET_KEY_HEX),
+ )
.await
.expect("enqueue");
let receipt_json = serde_json::to_value(&receipt).expect("receipt json");
@@ -1372,7 +1378,7 @@ async fn order_request_evidence_ingest_stores_request_and_enables_decision_enque
.expect("prepare decision");
let receipt = sdk
.orders()
- .enqueue_prepared_decision(
+ .enqueue_prepared_decision_with_explicit_signer(
&actor,
plan,
SdkRelayTargetPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
@@ -1758,7 +1764,10 @@ async fn order_decision_runtime_dtos_serialize_deterministically() {
let receipt = sdk
.orders()
- .enqueue_decision(enqueue_request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
+ .enqueue_decision_with_explicit_signer(
+ enqueue_request,
+ &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
+ )
.await
.expect("enqueue");
assert_eq!(receipt.workflow.kind, OrderWorkflowKind::Decision);
@@ -2088,7 +2097,7 @@ async fn order_decision_enqueue_accept_stores_event_queues_outbox_and_updates_st
let receipt = sdk
.orders()
- .enqueue_decision(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
+ .enqueue_decision_with_explicit_signer(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
.await
.expect("enqueue");
@@ -2190,7 +2199,7 @@ async fn order_decision_enqueue_decline_stores_event_and_status_sees_declined()
let receipt = sdk
.orders()
- .enqueue_decision(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
+ .enqueue_decision_with_explicit_signer(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
.await
.expect("enqueue");
@@ -2231,7 +2240,7 @@ async fn order_decision_enqueue_rejects_missing_request_evidence_before_mutation
let error = sdk
.orders()
- .enqueue_decision(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
+ .enqueue_decision_with_explicit_signer(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
.await
.expect_err("missing request evidence");
@@ -2276,7 +2285,7 @@ async fn order_decision_enqueue_returns_sanitized_signer_errors_before_decision_
let error = sdk
.orders()
- .enqueue_decision(request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
+ .enqueue_decision_with_explicit_signer(request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX))
.await
.expect_err("signer error");
let message = error.to_string();
@@ -2328,7 +2337,7 @@ async fn order_decision_enqueue_rejects_existing_decision_state_before_mutation(
let error = sdk
.orders()
- .enqueue_decision(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
+ .enqueue_decision_with_explicit_signer(request, &FixtureSigner::new(SELLER_SECRET_KEY_HEX))
.await
.expect_err("existing decision");
@@ -2383,7 +2392,7 @@ async fn order_revision_lifecycle_accepts_proposal_and_finalizes_agreement() {
.expect("prepare revision proposal");
let proposal_receipt = sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&proposal_actor,
proposal_plan,
SdkRelayTargetPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
@@ -2432,7 +2441,7 @@ async fn order_revision_lifecycle_accepts_proposal_and_finalizes_agreement() {
.expect("prepare revision decision");
let revision_decision_receipt = sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&revision_decision_actor,
revision_decision_plan,
SdkRelayTargetPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
@@ -2514,7 +2523,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
);
let proposal_receipt = sdk
.orders()
- .enqueue_revision_proposal(
+ .enqueue_revision_proposal_with_explicit_signer(
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
@@ -2556,7 +2565,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
let decision_error = sdk
.orders()
- .enqueue_decision(
+ .enqueue_decision_with_explicit_signer(
OrderDecisionEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
@@ -2581,7 +2590,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
);
let proposal_error = sdk
.orders()
- .enqueue_revision_proposal(
+ .enqueue_revision_proposal_with_explicit_signer(
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
@@ -2625,7 +2634,7 @@ async fn order_declined_revision_finalizes_declined_negotiation() {
);
let proposal_receipt = sdk
.orders()
- .enqueue_revision_proposal(
+ .enqueue_revision_proposal_with_explicit_signer(
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
@@ -2648,7 +2657,7 @@ async fn order_declined_revision_finalizes_declined_negotiation() {
);
let declined_revision_receipt = sdk
.orders()
- .enqueue_revision_decision(
+ .enqueue_revision_decision_with_explicit_signer(
OrderRevisionDecisionEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
@@ -2696,7 +2705,7 @@ async fn order_declined_revision_finalizes_declined_negotiation() {
);
let second_decision_error = sdk
.orders()
- .enqueue_revision_decision(
+ .enqueue_revision_decision_with_explicit_signer(
OrderRevisionDecisionEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
@@ -2745,7 +2754,7 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
.expect("prepare cancellation");
let cancellation = sdk
.orders()
- .enqueue_prepared_cancellation(
+ .enqueue_prepared_cancellation_with_explicit_signer(
&cancellation_actor,
cancellation_plan,
SdkRelayTargetPolicy::try_explicit([RELAY], SdkRelayUrlPolicy::Public)
@@ -2775,7 +2784,7 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
);
let replay = sdk
.orders()
- .enqueue_cancellation(
+ .enqueue_cancellation_with_explicit_signer(
OrderCancellationEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
@@ -2824,7 +2833,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
let request_event_id = RadrootsEventId::parse(request_event.id.as_str()).expect("request id");
let missing = sdk
.orders()
- .enqueue_revision_proposal(
+ .enqueue_revision_proposal_with_explicit_signer(
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
@@ -2858,7 +2867,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
.expect("ingest request");
let decision_receipt = sdk
.orders()
- .enqueue_decision(
+ .enqueue_decision_with_explicit_signer(
OrderDecisionEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
@@ -2882,7 +2891,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
);
let revision_error = sdk
.orders()
- .enqueue_revision_decision(
+ .enqueue_revision_decision_with_explicit_signer(
OrderRevisionDecisionEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
@@ -2903,7 +2912,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
let cancellation_error = sdk
.orders()
- .enqueue_cancellation(
+ .enqueue_cancellation_with_explicit_signer(
OrderCancellationEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
diff --git a/crates/sdk/tests/runtime_foundation.rs b/crates/sdk/tests/runtime_foundation.rs
@@ -779,6 +779,10 @@ fn sdk_examples_stay_on_product_api_boundary() {
"sdk_v1_local_enqueue_and_mock_sync",
include_str!("../examples/sdk_v1_local_enqueue_and_mock_sync.rs"),
),
+ (
+ "sdk_v1_myc_nip46_signer_setup",
+ include_str!("../examples/sdk_v1_myc_nip46_signer_setup.rs"),
+ ),
];
for (name, example) in examples {
@@ -800,7 +804,15 @@ fn sdk_examples_stay_on_product_api_boundary() {
assert!(local_enqueue.contains("SdkRelayTargetPolicy"));
assert!(local_enqueue.contains("SdkRelayTargetSet"));
assert!(local_enqueue.contains("SdkRelayUrlPolicy::Localhost"));
+ assert!(local_enqueue.contains("RadrootsSdkLocalKeySigner"));
+ assert!(local_enqueue.contains("RadrootsSdkSignerProvider::LocalKey"));
assert!(local_enqueue.contains("enqueue_prepared_publish"));
+ assert!(!local_enqueue.contains("enqueue_prepared_publish_with_explicit_signer"));
assert!(local_enqueue.contains("push_outbox_with_adapter"));
assert!(local_enqueue.contains("OrderStatusRequest"));
+
+ let myc_setup = include_str!("../examples/sdk_v1_myc_nip46_signer_setup.rs");
+ assert!(myc_setup.contains("RadrootsSdkMycNip46Signer"));
+ assert!(myc_setup.contains("RadrootsSdkSignerProvider::MycNip46"));
+ assert!(myc_setup.contains("radroots_sdk_myc_nip46_product_permission_strings"));
}
diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs
@@ -108,6 +108,19 @@ const REQUIRED_ORDERS_CLIENT_METHODS: &[&str] = &[
"pub async fn status(",
];
+const REQUIRED_ORDERS_CLIENT_ADVANCED_SIGNER_METHODS: &[&str] = &[
+ "pub async fn enqueue_submit_with_explicit_signer(",
+ "pub async fn enqueue_prepared_submit_with_explicit_signer(",
+ "pub async fn enqueue_decision_with_explicit_signer(",
+ "pub async fn enqueue_prepared_decision_with_explicit_signer(",
+ "pub async fn enqueue_revision_proposal_with_explicit_signer(",
+ "pub async fn enqueue_prepared_revision_proposal_with_explicit_signer(",
+ "pub async fn enqueue_revision_decision_with_explicit_signer(",
+ "pub async fn enqueue_prepared_revision_decision_with_explicit_signer(",
+ "pub async fn enqueue_cancellation_with_explicit_signer(",
+ "pub async fn enqueue_prepared_cancellation_with_explicit_signer(",
+];
+
const FORBIDDEN_ORDER_RUNTIME_PUBLIC_EXPORTS: &[&str] = &[
"CheckoutClient",
"EscrowClient",
@@ -289,6 +302,13 @@ fn orders_client_surface_is_inventory_guarded() {
"OrdersClient must expose inventory-guarded method `{method}`"
);
}
+
+ for method in REQUIRED_ORDERS_CLIENT_ADVANCED_SIGNER_METHODS {
+ assert!(
+ source.contains(method),
+ "OrdersClient must expose explicit-signer advanced method `{method}`"
+ );
+ }
}
#[test]
diff --git a/crates/sdk/tests/sync_runtime.rs b/crates/sdk/tests/sync_runtime.rs
@@ -461,7 +461,7 @@ async fn enqueue_listing_with_policy(
url_policy: SdkRelayUrlPolicy,
) -> i64 {
sdk.listings()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
ListingEnqueuePublishRequest::new(
actor(),
listing(d_tag, title),
@@ -1372,7 +1372,7 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved
let enqueue = sdk
.listings()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Proxy Coffee"),
@@ -1448,7 +1448,7 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() {
let enqueue = sdk
.listings()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Retry Coffee"),
diff --git a/crates/sdk/tests/unit/farms_runtime_tests.rs b/crates/sdk/tests/unit/farms_runtime_tests.rs
@@ -1,4 +1,6 @@
use super::*;
+use crate::{RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider};
+use radroots_nostr::prelude::RadrootsNostrKeys;
#[path = "../support/fixture_signer.rs"]
mod fixture_signer;
@@ -198,7 +200,7 @@ async fn farm_enqueue_publish_reports_prepare_errors_before_signing() {
.expect("sdk");
let error = sdk
.farms()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
FarmEnqueuePublishRequest::new(
farmer_actor(),
farm("AAAAAAAAAAAAAAAAAAAAA!", "Invalid Enqueue Farm"),
@@ -223,7 +225,7 @@ async fn farm_client_enqueue_methods_cover_source_attached_workflow_paths() {
let actor = farmer_actor();
let receipt = sdk
.farms()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
FarmEnqueuePublishRequest::new(
actor.clone(),
farm(FARM_A_D_TAG, "Enqueued Farm"),
@@ -248,7 +250,7 @@ async fn farm_client_enqueue_methods_cover_source_attached_workflow_paths() {
.expect("prepared farm");
let prepared = sdk
.farms()
- .enqueue_prepared_publish(
+ .enqueue_prepared_publish_with_explicit_signer(
&actor,
plan,
SdkRelayTargetPolicy::try_explicit([RELAY_B], SdkRelayUrlPolicy::Public)
@@ -261,3 +263,37 @@ async fn farm_client_enqueue_methods_cover_source_attached_workflow_paths() {
assert_eq!(prepared.signed_event_id, prepared.expected_event_id);
assert_eq!(prepared.local_event_seq, 2);
}
+
+#[tokio::test]
+async fn farm_configured_local_signer_enqueues_publish_without_explicit_signer() {
+ let keys = RadrootsNostrKeys::generate();
+ let farmer = keys.public_key().to_hex();
+ let sdk = crate::RadrootsSdk::builder()
+ .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_500))
+ .signer_provider(RadrootsSdkSignerProvider::LocalKey(
+ RadrootsSdkLocalKeySigner::new(keys).expect("signer"),
+ ))
+ .build()
+ .await
+ .expect("sdk");
+ let actor =
+ RadrootsActorContext::test(farmer.as_str(), [RadrootsActorRole::Farmer]).expect("actor");
+
+ let receipt = sdk
+ .farms()
+ .enqueue_publish(
+ FarmEnqueuePublishRequest::new(
+ actor,
+ farm(FARM_C_D_TAG, "Configured Farm"),
+ SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ .expect("target relays"),
+ )
+ .try_with_idempotency_key("farm-configured-local")
+ .expect("idempotency"),
+ )
+ .await
+ .expect("enqueue farm");
+
+ assert_eq!(receipt.signed_event_id, receipt.expected_event_id);
+ assert_eq!(receipt.state, SdkMutationState::StoredAndQueued);
+}
diff --git a/crates/sdk/tests/unit/listings_runtime_tests.rs b/crates/sdk/tests/unit/listings_runtime_tests.rs
@@ -1,4 +1,5 @@
use super::*;
+use crate::{RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider};
use radroots_core::{
RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
RadrootsCoreQuantityPrice, RadrootsCoreUnit,
@@ -10,6 +11,7 @@ use radroots_events::{
listing::{RadrootsListingBin, RadrootsListingProduct},
resource_area::RadrootsResourceAreaRef,
};
+use radroots_nostr::prelude::RadrootsNostrKeys;
#[path = "../support/fixture_signer.rs"]
mod fixture_signer;
@@ -32,11 +34,15 @@ fn actor() -> RadrootsActorContext {
}
fn listing(d_tag: &str, title: &str) -> RadrootsListing {
+ listing_for_seller(SELLER, d_tag, title)
+}
+
+fn listing_for_seller(seller: &str, d_tag: &str, title: &str) -> RadrootsListing {
RadrootsListing {
d_tag: RadrootsDTag::parse(d_tag).expect("d tag"),
published_at: None,
farm: RadrootsFarmRef {
- pubkey: SELLER.to_owned(),
+ pubkey: seller.to_owned(),
d_tag: FARM_D_TAG.to_owned(),
},
product: RadrootsListingProduct {
@@ -225,7 +231,7 @@ async fn listing_enqueue_publish_reports_prepare_errors_before_signing() {
.expect("sdk");
let error = sdk
.listings()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
ListingEnqueuePublishRequest::new(
actor(),
listing(LISTING_A_D_TAG, "Future Enqueue Greens"),
@@ -254,7 +260,7 @@ async fn listing_client_enqueue_methods_cover_source_attached_workflow_paths() {
let actor = actor();
let receipt = sdk
.listings()
- .enqueue_publish(
+ .enqueue_publish_with_explicit_signer(
ListingEnqueuePublishRequest::new(
actor.clone(),
listing(LISTING_A_D_TAG, "Enqueued Greens"),
@@ -279,7 +285,7 @@ async fn listing_client_enqueue_methods_cover_source_attached_workflow_paths() {
.expect("prepared listing");
let prepared = sdk
.listings()
- .enqueue_prepared_publish(
+ .enqueue_prepared_publish_with_explicit_signer(
&actor,
plan,
SdkRelayTargetPolicy::try_explicit([RELAY_B], SdkRelayUrlPolicy::Public)
@@ -292,3 +298,37 @@ async fn listing_client_enqueue_methods_cover_source_attached_workflow_paths() {
assert_eq!(prepared.signed_event_id, prepared.expected_event_id);
assert_eq!(prepared.local_event_seq, 2);
}
+
+#[tokio::test]
+async fn listing_configured_local_signer_enqueues_publish_without_explicit_signer() {
+ let keys = RadrootsNostrKeys::generate();
+ let seller = keys.public_key().to_hex();
+ let sdk = crate::RadrootsSdk::builder()
+ .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_500))
+ .signer_provider(RadrootsSdkSignerProvider::LocalKey(
+ RadrootsSdkLocalKeySigner::new(keys).expect("signer"),
+ ))
+ .build()
+ .await
+ .expect("sdk");
+ let actor =
+ RadrootsActorContext::test(seller.as_str(), [RadrootsActorRole::Seller]).expect("actor");
+
+ let receipt = sdk
+ .listings()
+ .enqueue_publish(
+ ListingEnqueuePublishRequest::new(
+ actor,
+ listing_for_seller(seller.as_str(), LISTING_C_D_TAG, "Configured Greens"),
+ SdkRelayTargetPolicy::try_explicit([RELAY_A], SdkRelayUrlPolicy::Public)
+ .expect("target relays"),
+ )
+ .try_with_idempotency_key("listing-configured-local")
+ .expect("idempotency"),
+ )
+ .await
+ .expect("enqueue listing");
+
+ assert_eq!(receipt.signed_event_id, receipt.expected_event_id);
+ assert_eq!(receipt.state, SdkMutationState::StoredAndQueued);
+}
diff --git a/crates/sdk/tests/unit/orders_runtime_tests.rs b/crates/sdk/tests/unit/orders_runtime_tests.rs
@@ -1,5 +1,5 @@
use super::*;
-use crate::RadrootsSdk;
+use crate::{RadrootsSdk, RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider};
use radroots_authority::{RadrootsSignerError, RadrootsSignerIdentity};
use radroots_core::{
RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreUnit,
@@ -260,8 +260,7 @@ struct OrderFixtureSigner {
impl OrderFixtureSigner {
fn new(secret_key_hex: &str) -> Self {
- let secret_key = RadrootsNostrSecretKey::from_hex(secret_key_hex).expect("secret key");
- let keys = RadrootsNostrKeys::new(secret_key);
+ let keys = keys_from_secret(secret_key_hex);
let pubkey = keys.public_key().to_hex();
Self {
identity: RadrootsSignerIdentity::new(pubkey).expect("identity"),
@@ -270,6 +269,11 @@ impl OrderFixtureSigner {
}
}
+fn keys_from_secret(secret_key_hex: &str) -> RadrootsNostrKeys {
+ let secret_key = RadrootsNostrSecretKey::from_hex(secret_key_hex).expect("secret key");
+ RadrootsNostrKeys::new(secret_key)
+}
+
impl RadrootsEventSigner for OrderFixtureSigner {
fn pubkey(&self) -> &RadrootsPublicKey {
self.identity.pubkey()
@@ -411,6 +415,32 @@ async fn prepared_order_sdk() -> RadrootsSdk {
.expect("sdk")
}
+#[tokio::test]
+async fn order_configured_local_signer_enqueues_submit_without_explicit_signer() {
+ let sdk = RadrootsSdk::builder()
+ .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000))
+ .signer_provider(RadrootsSdkSignerProvider::LocalKey(
+ RadrootsSdkLocalKeySigner::new(keys_from_secret(BUYER_SECRET_KEY_HEX)).expect("signer"),
+ ))
+ .build()
+ .await
+ .expect("sdk");
+
+ let receipt = sdk
+ .orders()
+ .enqueue_submit(OrderSubmitEnqueueRequest::new(
+ fixture_buyer_actor(),
+ fixture_event_ptr('a'),
+ fixture_order_request("order-configured-local-1"),
+ fixture_target_relays(),
+ ))
+ .await
+ .expect("enqueue submit");
+
+ assert_eq!(receipt.signed_event_id, receipt.expected_event_id);
+ assert_eq!(receipt.state, SdkMutationState::StoredAndQueued);
+}
+
async fn enqueue_fixture_submit(sdk: &RadrootsSdk, raw_order_id: &str) -> OrderSubmitReceipt {
let buyer = fixture_buyer_actor();
let plan = sdk
@@ -422,7 +452,7 @@ async fn enqueue_fixture_submit(sdk: &RadrootsSdk, raw_order_id: &str) -> OrderS
))
.expect("submit plan");
sdk.orders()
- .enqueue_prepared_submit(
+ .enqueue_prepared_submit_with_explicit_signer(
&buyer,
plan,
fixture_target_relays(),
@@ -2069,7 +2099,7 @@ async fn prepared_submit_and_decision_enqueue_cover_source_attached_success_path
.expect("decision plan");
let decision = sdk
.orders()
- .enqueue_prepared_decision(
+ .enqueue_prepared_decision_with_explicit_signer(
&seller,
decision_plan,
fixture_target_relays(),
@@ -2105,7 +2135,7 @@ async fn prepared_revision_lifecycle_enqueue_cover_source_attached_success_paths
.expect("proposal plan");
let proposal = sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan,
fixture_target_relays(),
@@ -2133,7 +2163,7 @@ async fn prepared_revision_lifecycle_enqueue_cover_source_attached_success_paths
.expect("revision decision plan");
let revision = sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&buyer,
revision_decision_plan,
fixture_target_relays(),
@@ -2165,7 +2195,7 @@ async fn prepared_cancellation_enqueue_covers_source_attached_success_path() {
.expect("cancellation plan");
let cancellation = sdk
.orders()
- .enqueue_prepared_cancellation(
+ .enqueue_prepared_cancellation_with_explicit_signer(
&buyer,
cancellation_plan,
fixture_target_relays(),
@@ -2186,7 +2216,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
let sdk = prepared_order_sdk().await;
let decision_submit = sdk
.orders()
- .enqueue_submit(
+ .enqueue_submit_with_explicit_signer(
OrderSubmitEnqueueRequest::new(
fixture_buyer_actor(),
fixture_event_ptr('b'),
@@ -2199,7 +2229,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
.expect("enqueue submit");
let decision = sdk
.orders()
- .enqueue_decision(
+ .enqueue_decision_with_explicit_signer(
OrderDecisionEnqueueRequest::new(
fixture_seller_actor(),
fixture_order_event_ptr(&decision_submit.signed_event_id),
@@ -2214,7 +2244,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
let revision_submit = sdk
.orders()
- .enqueue_submit(
+ .enqueue_submit_with_explicit_signer(
OrderSubmitEnqueueRequest::new(
fixture_buyer_actor(),
fixture_event_ptr('c'),
@@ -2232,7 +2262,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
);
let proposal = sdk
.orders()
- .enqueue_revision_proposal(
+ .enqueue_revision_proposal_with_explicit_signer(
OrderRevisionProposalEnqueueRequest::new(
fixture_seller_actor(),
fixture_order_event_ptr(&revision_submit.signed_event_id),
@@ -2246,7 +2276,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
.expect("enqueue proposal");
let revision = sdk
.orders()
- .enqueue_revision_decision(
+ .enqueue_revision_decision_with_explicit_signer(
OrderRevisionDecisionEnqueueRequest::new(
fixture_buyer_actor(),
fixture_order_event_ptr(&revision_submit.signed_event_id),
@@ -2262,7 +2292,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
let cancellation_submit = sdk
.orders()
- .enqueue_submit(
+ .enqueue_submit_with_explicit_signer(
OrderSubmitEnqueueRequest::new(
fixture_buyer_actor(),
fixture_event_ptr('d'),
@@ -2275,7 +2305,7 @@ async fn convenience_order_enqueue_methods_cover_source_attached_wrappers() {
.expect("enqueue cancellation submit");
let cancellation = sdk
.orders()
- .enqueue_cancellation(
+ .enqueue_cancellation_with_explicit_signer(
OrderCancellationEnqueueRequest::new(
fixture_buyer_actor(),
fixture_order_event_ptr(&cancellation_submit.signed_event_id),
@@ -2315,7 +2345,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
.expect("decision plan");
let decision_missing = sdk
.orders()
- .enqueue_prepared_decision(
+ .enqueue_prepared_decision_with_explicit_signer(
&seller,
decision_plan,
fixture_target_relays(),
@@ -2340,7 +2370,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
.expect("proposal plan");
let proposal_missing = sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan,
fixture_target_relays(),
@@ -2365,7 +2395,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
.expect("revision decision plan");
let revision_missing = sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&buyer,
revision_decision_plan,
fixture_target_relays(),
@@ -2390,7 +2420,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
.expect("cancellation plan");
let cancellation_missing = sdk
.orders()
- .enqueue_prepared_cancellation(
+ .enqueue_prepared_cancellation_with_explicit_signer(
&buyer,
cancellation_plan,
fixture_target_relays(),
@@ -2452,7 +2482,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
closed_sdk._event_store.pool().close().await;
let closed_error = closed_sdk
.orders()
- .enqueue_prepared_decision(
+ .enqueue_prepared_decision_with_explicit_signer(
&seller,
closed_plan,
fixture_target_relays(),
@@ -2464,7 +2494,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
assert!(matches!(closed_error, RadrootsSdkError::EventStore { .. }));
let closed_proposal_error = closed_sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
closed_proposal_plan,
fixture_target_relays(),
@@ -2479,7 +2509,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
));
let closed_revision_error = closed_sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&buyer,
closed_revision_plan,
fixture_target_relays(),
@@ -2494,7 +2524,7 @@ async fn prepared_lifecycle_enqueues_report_missing_and_closed_preflight_errors(
));
let closed_cancellation_error = closed_sdk
.orders()
- .enqueue_prepared_cancellation(
+ .enqueue_prepared_cancellation_with_explicit_signer(
&buyer,
closed_cancellation_plan,
fixture_target_relays(),
@@ -2605,7 +2635,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
proposal_sdk._outbox.pool().close().await;
let proposal_error = proposal_sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan,
fixture_target_relays(),
@@ -2635,7 +2665,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
.expect("revision proposal plan");
let proposal = revision_sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan,
fixture_target_relays(),
@@ -2657,7 +2687,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
revision_sdk._outbox.pool().close().await;
let revision_error = revision_sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&buyer,
revision_plan,
fixture_target_relays(),
@@ -2683,7 +2713,7 @@ async fn prepared_lifecycle_enqueues_report_closed_outbox_after_preflight() {
cancellation_sdk._outbox.pool().close().await;
let cancellation_error = cancellation_sdk
.orders()
- .enqueue_prepared_cancellation(
+ .enqueue_prepared_cancellation_with_explicit_signer(
&buyer,
cancellation_plan,
fixture_target_relays(),
@@ -2710,7 +2740,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("decision plan");
decision_sdk
.orders()
- .enqueue_prepared_decision(
+ .enqueue_prepared_decision_with_explicit_signer(
&seller,
decision_plan.clone(),
fixture_target_relays(),
@@ -2721,7 +2751,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("enqueue decision");
let decision_repeat = decision_sdk
.orders()
- .enqueue_prepared_decision(
+ .enqueue_prepared_decision_with_explicit_signer(
&seller,
decision_plan,
fixture_target_relays(),
@@ -2759,7 +2789,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("proposal plan");
proposal_sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan.clone(),
fixture_target_relays(),
@@ -2770,7 +2800,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("enqueue proposal");
let proposal_repeat = proposal_sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan,
fixture_target_relays(),
@@ -2808,7 +2838,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("revision proposal plan");
let proposal = revision_sdk
.orders()
- .enqueue_prepared_revision_proposal(
+ .enqueue_prepared_revision_proposal_with_explicit_signer(
&seller,
proposal_plan,
fixture_target_relays(),
@@ -2829,7 +2859,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("revision plan");
revision_sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&buyer,
revision_plan.clone(),
fixture_target_relays(),
@@ -2840,7 +2870,7 @@ async fn prepared_lifecycle_enqueues_skip_preflight_for_existing_events() {
.expect("enqueue revision");
let revision_repeat = revision_sdk
.orders()
- .enqueue_prepared_revision_decision(
+ .enqueue_prepared_revision_decision_with_explicit_signer(
&buyer,
revision_plan,
fixture_target_relays(),
@@ -2887,7 +2917,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
));
assert!(matches!(
sdk.orders()
- .enqueue_submit(
+ .enqueue_submit_with_explicit_signer(
OrderSubmitEnqueueRequest::new(
buyer.clone(),
fixture_event_ptr('a'),
@@ -2902,7 +2932,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
));
assert!(matches!(
sdk.orders()
- .enqueue_decision(
+ .enqueue_decision_with_explicit_signer(
OrderDecisionEnqueueRequest::new(
seller.clone(),
fixture_event_ptr('b'),
@@ -2925,7 +2955,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
);
assert!(matches!(
sdk.orders()
- .enqueue_revision_proposal(
+ .enqueue_revision_proposal_with_explicit_signer(
OrderRevisionProposalEnqueueRequest::new(
seller.clone(),
ptr(root_event_id.as_str().to_owned()),
@@ -2941,7 +2971,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
));
assert!(matches!(
sdk.orders()
- .enqueue_revision_decision(
+ .enqueue_revision_decision_with_explicit_signer(
OrderRevisionDecisionEnqueueRequest::new(
buyer.clone(),
ptr(root_event_id.as_str().to_owned()),
@@ -2957,7 +2987,7 @@ async fn order_ingest_and_enqueue_wrappers_report_prepare_timestamp_errors() {
));
assert!(matches!(
sdk.orders()
- .enqueue_cancellation(
+ .enqueue_cancellation_with_explicit_signer(
OrderCancellationEnqueueRequest::new(
buyer,
ptr(root_event_id.as_str().to_owned()),
diff --git a/crates/sdk/tests/unit/signer_provider_tests.rs b/crates/sdk/tests/unit/signer_provider_tests.rs
@@ -160,6 +160,7 @@ async fn local_key_provider_signs_authorized_frozen_draft() {
assert_eq!(provider.mode(), RadrootsSdkSignerMode::LocalKey);
assert_eq!(provider.status(), signer.status());
+ assert!(provider.capability().nip46_permissions.is_empty());
assert_eq!(receipt.mode, RadrootsSdkSignerMode::LocalKey);
assert_eq!(receipt.signer_pubkey, USER_PUBLIC_KEY_HEX);
assert_eq!(receipt.signed_event_id, draft.expected_event_id);
@@ -176,6 +177,22 @@ async fn local_key_provider_signs_authorized_frozen_draft() {
);
}
+#[test]
+fn myc_nip46_product_permissions_cover_sdk_write_event_kinds() {
+ let permissions = radroots_sdk_myc_nip46_product_permissions();
+ let rendered = radroots_sdk_myc_nip46_product_permission_strings();
+
+ assert_eq!(
+ permissions.as_slice().len(),
+ RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS.len()
+ );
+ assert_eq!(rendered.len(), permissions.as_slice().len());
+ for kind in RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS {
+ assert!(rendered.contains(&format!("sign_event:{kind}")));
+ }
+ assert!(!rendered.contains(&"sign_event:1".to_owned()));
+}
+
#[tokio::test]
async fn myc_nip46_provider_signs_and_validates_remote_event() {
let client_keys = client_keys();
@@ -200,6 +217,10 @@ async fn myc_nip46_provider_signs_and_validates_remote_event() {
RadrootsSdkMycNip46Signer::new(client_keys, target, USER_PUBLIC_KEY_HEX, transport.clone())
.expect("signer");
let provider = RadrootsSdkSignerProvider::MycNip46(signer);
+ assert_eq!(
+ provider.capability().nip46_permissions,
+ radroots_sdk_myc_nip46_product_permission_strings()
+ );
let actor = actor();
let mut progress = Vec::new();