sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

commit 402732b862f40142bdd6445a4d5b8187c2f0a45d
parent 26e03efae10d928d57a0a979be1f39327ade52d4
Author: triesap <tyson@radroots.org>
Date:   Tue, 30 Jun 2026 07:01:29 +0000

sdk: remove public protocol bypass surface

- delete the public protocol re-export module and stale facade test
- move examples and runtime tests to radroots_events and radroots_events_codec contracts
- trim private order helper methods left unused by the facade removal
- validated with cargo test -p radroots_sdk --all-features

Diffstat:
Mcrates/sdk/examples/runtime_local.rs | 6++----
Mcrates/sdk/examples/sdk_v1_listing_prepare.rs | 6++----
Mcrates/sdk/examples/sdk_v1_local_enqueue_and_mock_sync.rs | 6++----
Mcrates/sdk/src/lib.rs | 3---
Dcrates/sdk/src/listing.rs | 49-------------------------------------------------
Mcrates/sdk/src/order.rs | 69+--------------------------------------------------------------------
Dcrates/sdk/src/profile.rs | 13-------------
Dcrates/sdk/src/protocol/events.rs | 4----
Dcrates/sdk/src/protocol/farm.rs | 1-
Dcrates/sdk/src/protocol/identity.rs | 1-
Dcrates/sdk/src/protocol/listing.rs | 1-
Dcrates/sdk/src/protocol/mod.rs | 10----------
Dcrates/sdk/src/protocol/order.rs | 1-
Dcrates/sdk/src/protocol/profile.rs | 1-
Dcrates/sdk/src/protocol/wire.rs | 8--------
Mcrates/sdk/tests/dvm_runtime.rs | 13++++++-------
Dcrates/sdk/tests/facade.rs | 512-------------------------------------------------------------------------------
Mcrates/sdk/tests/orders_runtime.rs | 48++++++++++++++++++++++++------------------------
Mcrates/sdk/tests/replica_ingest.rs | 6+++---
Mcrates/sdk/tests/source_boundary.rs | 36+++++++++++++++++++++++++++---------
Mcrates/sdk/tests/trade_product_publish_runtime.rs | 16++++++++++------
21 files changed, 77 insertions(+), 733 deletions(-)

diff --git a/crates/sdk/examples/runtime_local.rs b/crates/sdk/examples/runtime_local.rs @@ -4,12 +4,10 @@ use radroots_core::{ RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; use radroots_events::contract::RadrootsActorRole; +use radroots_events::farm::RadrootsFarmRef; use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId}; +use radroots_events::listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct}; use radroots_nostr::prelude::RadrootsNostrKeys; -use radroots_sdk::protocol::farm::RadrootsFarmRef; -use radroots_sdk::protocol::listing::{ - RadrootsListing, RadrootsListingBin, RadrootsListingProduct, -}; use radroots_sdk::{ ListingPreparePublishRequest, PushOutboxRequest, RadrootsClient, RadrootsSdkError, RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SdkIdempotencyKey, diff --git a/crates/sdk/examples/sdk_v1_listing_prepare.rs b/crates/sdk/examples/sdk_v1_listing_prepare.rs @@ -4,11 +4,9 @@ use radroots_core::{ RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; use radroots_events::contract::RadrootsActorRole; +use radroots_events::farm::RadrootsFarmRef; use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId}; -use radroots_sdk::protocol::farm::RadrootsFarmRef; -use radroots_sdk::protocol::listing::{ - RadrootsListing, RadrootsListingBin, RadrootsListingProduct, -}; +use radroots_events::listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct}; use radroots_sdk::{ListingPreparePublishRequest, RadrootsClient, RadrootsSdkTimestamp}; const SELLER: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 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 @@ -4,13 +4,11 @@ use radroots_core::{ RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; use radroots_events::contract::RadrootsActorRole; +use radroots_events::farm::RadrootsFarmRef; use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId}; +use radroots_events::listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct}; use radroots_nostr::prelude::RadrootsNostrKeys; use radroots_relay_transport::RadrootsMockRelayPublishAdapter; -use radroots_sdk::protocol::farm::RadrootsFarmRef; -use radroots_sdk::protocol::listing::{ - RadrootsListing, RadrootsListingBin, RadrootsListingProduct, -}; use radroots_sdk::{ ListingPreparePublishRequest, PushOutboxRequest, RadrootsClient, RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SdkIdempotencyKey, SdkRelayTargetPolicy, diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs @@ -26,7 +26,6 @@ mod geonames; mod idempotency; #[cfg(feature = "identity-models")] mod identity; -mod listing; #[cfg(feature = "runtime")] mod listings_runtime; #[cfg(feature = "runtime")] @@ -40,8 +39,6 @@ mod privacy; mod private_store; #[cfg(feature = "runtime")] mod product_clients; -mod profile; -pub mod protocol; #[cfg(feature = "runtime")] mod relay_targets; #[cfg(feature = "runtime")] diff --git a/crates/sdk/src/listing.rs b/crates/sdk/src/listing.rs @@ -1,49 +0,0 @@ -pub use radroots_events::listing::*; -pub use radroots_events::order::RadrootsListingParseError; -pub use radroots_events::trade_validation::RadrootsTradeValidationListingError; -pub use radroots_events_codec::error::EventEncodeError; -pub use radroots_trade::listing::validation::RadrootsTradeListing as TradeListingValidateResult; - -#[cfg(not(feature = "std"))] -use alloc::{string::String, vec::Vec}; -#[cfg(feature = "std")] -use std::{string::String, vec::Vec}; - -#[cfg(feature = "serde_json")] -use radroots_events::RadrootsNostrEvent; -use radroots_events_codec::wire::WireEventParts; - -pub type NostrTags = Vec<Vec<String>>; - -#[derive(Debug, Clone)] -pub struct RadrootsListingDraft { - parts: WireEventParts, -} - -impl RadrootsListingDraft { - pub fn as_wire_parts(&self) -> &WireEventParts { - &self.parts - } - - pub fn into_wire_parts(self) -> WireEventParts { - self.parts - } -} - -pub fn build_tags(listing: &RadrootsListing) -> Result<NostrTags, EventEncodeError> { - radroots_events_codec::listing::encode::listing_build_tags(listing) -} - -#[cfg(feature = "serde_json")] -pub fn build_draft(listing: &RadrootsListing) -> Result<RadrootsListingDraft, EventEncodeError> { - Ok(RadrootsListingDraft { - parts: radroots_events_codec::listing::encode::to_wire_parts(listing)?, - }) -} - -#[cfg(feature = "serde_json")] -pub fn parse_event( - event: &RadrootsNostrEvent, -) -> Result<RadrootsListing, RadrootsListingParseError> { - radroots_trade::listing::parse_listing_event(event) -} diff --git a/crates/sdk/src/order.rs b/crates/sdk/src/order.rs @@ -1,12 +1,7 @@ -pub use radroots_events::ids::{RadrootsIdParseError, RadrootsListingAddress}; pub use radroots_events::order::*; -pub use radroots_events::trade_validation::*; pub use radroots_events_codec::error::EventEncodeError; #[cfg(feature = "serde_json")] -pub use radroots_events_codec::order::{ - RadrootsOrderEnvelopeParseError, RadrootsOrderEventContext, -}; -pub use radroots_trade::listing::validation::RadrootsTradeListing as TradeListingValidateResult; +pub use radroots_events_codec::order::RadrootsOrderEnvelopeParseError; #[cfg(feature = "serde_json")] use radroots_events::{RadrootsNostrEvent, RadrootsNostrEventPtr, ids::RadrootsEventId}; @@ -38,50 +33,30 @@ pub struct RadrootsOrderCancellationDraft { } impl RadrootsOrderRequestDraft { - pub fn as_wire_parts(&self) -> &WireEventParts { - &self.parts - } - pub fn into_wire_parts(self) -> WireEventParts { self.parts } } impl RadrootsOrderDecisionDraft { - pub fn as_wire_parts(&self) -> &WireEventParts { - &self.parts - } - pub fn into_wire_parts(self) -> WireEventParts { self.parts } } impl RadrootsOrderRevisionProposalDraft { - pub fn as_wire_parts(&self) -> &WireEventParts { - &self.parts - } - pub fn into_wire_parts(self) -> WireEventParts { self.parts } } impl RadrootsOrderRevisionDecisionDraft { - pub fn as_wire_parts(&self) -> &WireEventParts { - &self.parts - } - pub fn into_wire_parts(self) -> WireEventParts { self.parts } } impl RadrootsOrderCancellationDraft { - pub fn as_wire_parts(&self) -> &WireEventParts { - &self.parts - } - pub fn into_wire_parts(self) -> WireEventParts { self.parts } @@ -163,45 +138,3 @@ pub fn parse_order_request( ) -> Result<RadrootsOrderEnvelope<RadrootsOrderRequest>, RadrootsOrderEnvelopeParseError> { radroots_events_codec::order::order_request_from_event(event) } - -#[cfg(feature = "serde_json")] -pub fn parse_order_decision( - event: &RadrootsNostrEvent, -) -> Result<RadrootsOrderEnvelope<RadrootsOrderDecision>, RadrootsOrderEnvelopeParseError> { - radroots_events_codec::order::order_decision_from_event(event) -} - -#[cfg(feature = "serde_json")] -pub fn parse_order_revision_proposal( - event: &RadrootsNostrEvent, -) -> Result<RadrootsOrderEnvelope<RadrootsOrderRevisionProposal>, RadrootsOrderEnvelopeParseError> { - radroots_events_codec::order::order_revision_proposal_from_event(event) -} - -#[cfg(feature = "serde_json")] -pub fn parse_order_revision_decision( - event: &RadrootsNostrEvent, -) -> Result<RadrootsOrderEnvelope<RadrootsOrderRevisionDecision>, RadrootsOrderEnvelopeParseError> { - radroots_events_codec::order::order_revision_decision_from_event(event) -} - -#[cfg(feature = "serde_json")] -pub fn parse_order_cancellation( - event: &RadrootsNostrEvent, -) -> Result<RadrootsOrderEnvelope<RadrootsOrderCancellation>, RadrootsOrderEnvelopeParseError> { - radroots_events_codec::order::order_cancellation_from_event(event) -} - -#[cfg(feature = "serde_json")] -pub fn parse_listing_address( - listing_addr: &str, -) -> Result<RadrootsListingAddress, RadrootsIdParseError> { - RadrootsListingAddress::parse(listing_addr) -} - -#[cfg(feature = "serde_json")] -pub fn validate_listing_event( - event: &RadrootsNostrEvent, -) -> Result<TradeListingValidateResult, RadrootsTradeValidationListingError> { - radroots_trade::listing::validation::validate_listing_event(event) -} diff --git a/crates/sdk/src/profile.rs b/crates/sdk/src/profile.rs @@ -1,13 +0,0 @@ -pub use radroots_events::profile::{RadrootsProfile, RadrootsProfileType}; -pub use radroots_events_codec::profile::error::ProfileEncodeError; - -#[cfg(feature = "serde_json")] -use radroots_events_codec::wire::WireEventParts; - -#[cfg(feature = "serde_json")] -pub fn build_draft( - profile: &RadrootsProfile, - profile_type: Option<RadrootsProfileType>, -) -> Result<WireEventParts, ProfileEncodeError> { - radroots_events_codec::profile::encode::to_wire_parts_with_profile_type(profile, profile_type) -} diff --git a/crates/sdk/src/protocol/events.rs b/crates/sdk/src/protocol/events.rs @@ -1,4 +0,0 @@ -pub use radroots_events::{ - RadrootsNostrEvent, RadrootsNostrEventPtr, RadrootsNostrEventRef, - draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent}, -}; diff --git a/crates/sdk/src/protocol/farm.rs b/crates/sdk/src/protocol/farm.rs @@ -1 +0,0 @@ -pub use crate::farm::*; diff --git a/crates/sdk/src/protocol/identity.rs b/crates/sdk/src/protocol/identity.rs @@ -1 +0,0 @@ -pub use crate::identity::*; diff --git a/crates/sdk/src/protocol/listing.rs b/crates/sdk/src/protocol/listing.rs @@ -1 +0,0 @@ -pub use crate::listing::*; diff --git a/crates/sdk/src/protocol/mod.rs b/crates/sdk/src/protocol/mod.rs @@ -1,10 +0,0 @@ -#![forbid(unsafe_code)] - -pub mod events; -pub mod farm; -#[cfg(feature = "identity-models")] -pub mod identity; -pub mod listing; -pub mod order; -pub mod profile; -pub mod wire; diff --git a/crates/sdk/src/protocol/order.rs b/crates/sdk/src/protocol/order.rs @@ -1 +0,0 @@ -pub use crate::order::*; diff --git a/crates/sdk/src/protocol/profile.rs b/crates/sdk/src/protocol/profile.rs @@ -1 +0,0 @@ -pub use crate::profile::*; diff --git a/crates/sdk/src/protocol/wire.rs b/crates/sdk/src/protocol/wire.rs @@ -1,8 +0,0 @@ -#[cfg(not(feature = "std"))] -use alloc::{string::String, vec::Vec}; -#[cfg(feature = "std")] -use std::{string::String, vec::Vec}; - -pub use radroots_events_codec::wire::WireEventParts; - -pub type NostrTags = Vec<Vec<String>>; diff --git a/crates/sdk/tests/dvm_runtime.rs b/crates/sdk/tests/dvm_runtime.rs @@ -8,7 +8,7 @@ use radroots_core::{ }; use radroots_event_store::{RadrootsEventIngest, RadrootsEventStore}; use radroots_events::{ - RadrootsNostrEvent, + RadrootsNostrEvent, RadrootsNostrEventPtr, contract::RadrootsActorRole, draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent}, ids::{ @@ -27,7 +27,6 @@ use radroots_nostr::prelude::{ radroots_nostr_build_event, radroots_nostr_sign_frozen_draft, }; use radroots_outbox::RadrootsOutbox; -use radroots_sdk::protocol::events::RadrootsNostrEventPtr; use radroots_sdk::{ DVM_TRADE_TRANSITION_PROOF_REQUEST_OPERATION_KIND, DvmTradeTransitionProofEnqueueRequest, DvmTradeTransitionProofPrepareRequest, DvmValidationReceiptIngestRequest, RadrootsClient, @@ -761,12 +760,12 @@ fn order_decision(raw_order_id: &str) -> RadrootsOrderDecision { } fn signed_order_request_event(raw_order_id: &str, created_at: u32) -> RadrootsNostrEvent { - let draft = radroots_sdk::protocol::order::build_order_request_draft( + let draft = radroots_events_codec::order::order_request_event_build( &listing_event_ptr(), &order_request(raw_order_id), ) .expect("request draft"); - signed_event(BUYER_SECRET_KEY_HEX, created_at, draft.into_wire_parts()) + signed_event(BUYER_SECRET_KEY_HEX, created_at, draft) } fn signed_order_decision_event( @@ -774,13 +773,13 @@ fn signed_order_decision_event( root_event_id: &RadrootsEventId, created_at: u32, ) -> RadrootsNostrEvent { - let draft = radroots_sdk::protocol::order::build_order_decision_draft( + let draft = radroots_events_codec::order::order_decision_event_build( root_event_id, root_event_id, &order_decision(raw_order_id), ) .expect("decision draft"); - signed_event(SELLER_SECRET_KEY_HEX, created_at, draft.into_wire_parts()) + signed_event(SELLER_SECRET_KEY_HEX, created_at, draft) } fn signed_validation_receipt_event( @@ -823,7 +822,7 @@ fn signed_validation_receipt_event( fn signed_event( secret_key_hex: &str, created_at: u32, - parts: radroots_sdk::protocol::wire::WireEventParts, + parts: radroots_events_codec::wire::WireEventParts, ) -> RadrootsNostrEvent { let secret_key = RadrootsNostrSecretKey::from_hex(secret_key_hex).expect("secret key"); let keys = RadrootsNostrKeys::new(secret_key); diff --git a/crates/sdk/tests/facade.rs b/crates/sdk/tests/facade.rs @@ -1,512 +0,0 @@ -use radroots_core::{ - RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity, - RadrootsCoreQuantityPrice, RadrootsCoreUnit, -}; -use radroots_events::farm::{RadrootsFarm, RadrootsFarmRef}; -use radroots_events::ids::{RadrootsEventId, 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, KIND_PROFILE, -}; -use radroots_events::listing::{ - RadrootsListing, RadrootsListingAvailability, RadrootsListingBin, - RadrootsListingDeliveryMethod, RadrootsListingProduct, RadrootsListingPublicLocation, - RadrootsListingStatus, -}; -use radroots_events::order::{ - RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderDecisionOutcome, - RadrootsOrderEconomicItem, RadrootsOrderEconomics, RadrootsOrderInventoryCommitment, - RadrootsOrderItem, RadrootsOrderPricingBasis, RadrootsOrderRequest, - RadrootsOrderRevisionDecision, RadrootsOrderRevisionOutcome, RadrootsOrderRevisionProposal, -}; -use radroots_events::profile::{RadrootsProfile, RadrootsProfileType}; -use radroots_events::resource_area::RadrootsResourceAreaRef; -use radroots_sdk::protocol::events::{RadrootsNostrEvent, RadrootsNostrEventPtr}; -use radroots_sdk::protocol::wire::WireEventParts; -use radroots_sdk::protocol::{farm, listing, order, profile}; - -fn sample_profile() -> RadrootsProfile { - RadrootsProfile { - name: "North Farm".into(), - display_name: Some("North Farm".into()), - nip05: None, - about: Some("Organic coffee".into()), - website: Some("https://example.com".into()), - picture: None, - banner: None, - lud06: None, - lud16: None, - bot: None, - } -} - -fn sample_farm() -> RadrootsFarm { - RadrootsFarm { - d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(), - name: "North Farm".into(), - about: Some("Organic coffee".into()), - website: None, - picture: None, - banner: None, - location: None, - tags: Some(vec!["coffee".into()]), - } -} - -fn sample_listing() -> RadrootsListing { - RadrootsListing { - d_tag: "AAAAAAAAAAAAAAAAAAAAAg".parse().expect("listing d tag"), - published_at: None, - farm: RadrootsFarmRef { - pubkey: "a".repeat(64), - d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(), - }, - product: RadrootsListingProduct { - key: "coffee".into(), - title: "Coffee".into(), - category: "coffee".into(), - summary: Some("Single origin coffee".into()), - process: None, - lot: None, - location: None, - profile: None, - year: None, - }, - primary_bin_id: "bin-1".parse().expect("primary bin id"), - bins: vec![RadrootsListingBin { - bin_id: "bin-1".parse().expect("bin id"), - quantity: RadrootsCoreQuantity::new( - RadrootsCoreDecimal::from(1000u32), - RadrootsCoreUnit::MassG, - ), - price_per_canonical_unit: RadrootsCoreQuantityPrice { - amount: RadrootsCoreMoney::new( - RadrootsCoreDecimal::from(20u32), - RadrootsCoreCurrency::USD, - ), - quantity: RadrootsCoreQuantity::new( - RadrootsCoreDecimal::from(1u32), - RadrootsCoreUnit::MassG, - ), - }, - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - }], - resource_area: None, - plot: None, - discounts: None, - inventory_available: Some(RadrootsCoreDecimal::from(5u32)), - availability: Some(RadrootsListingAvailability::Status { - status: RadrootsListingStatus::Active, - }), - delivery_method: Some(RadrootsListingDeliveryMethod::Pickup), - location: Some(RadrootsListingPublicLocation { - primary: "North Farm".into(), - city: Some("San Francisco".into()), - region: Some("CA".into()), - country: Some("US".into()), - geohash: "9q8yy".into(), - }), - images: None, - } -} - -fn listing_event(listing_value: &RadrootsListing) -> RadrootsNostrEvent { - let parts = listing::build_draft(listing_value).expect("listing draft"); - RadrootsNostrEvent { - id: "event-1".into(), - author: listing_value.farm.pubkey.clone(), - created_at: 1, - kind: parts.as_wire_parts().kind, - tags: parts.as_wire_parts().tags.clone(), - content: parts.as_wire_parts().content.clone(), - sig: String::new(), - } -} - -#[test] -fn listing_facade_rejects_malformed_resource_area_refs() { - let mut listing_value = sample_listing(); - listing_value.resource_area = Some(RadrootsResourceAreaRef { - pubkey: "a".repeat(64), - d_tag: "bad d tag".to_owned(), - }); - - assert!(listing::build_draft(&listing_value).is_err()); -} - -fn listing_event_ptr() -> RadrootsNostrEventPtr { - RadrootsNostrEventPtr { - id: core::iter::repeat_n('a', 64).collect(), - relays: Some("wss://listing.relay.example".into()), - } -} - -fn public_key(character: char) -> RadrootsPublicKey { - core::iter::repeat_n(character, 64) - .collect::<String>() - .parse() - .expect("public key") -} - -fn event_id(character: char) -> RadrootsEventId { - core::iter::repeat_n(character, 64) - .collect::<String>() - .parse() - .expect("event id") -} - -fn sample_order_request() -> RadrootsOrderRequest { - let seller_pubkey = public_key('a'); - - RadrootsOrderRequest { - order_id: "order-1".parse().expect("order id"), - listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") - .parse() - .expect("listing address"), - buyer_pubkey: public_key('b'), - seller_pubkey, - items: vec![RadrootsOrderItem { - bin_id: "bin-1".parse().expect("bin id"), - bin_count: 2, - }], - economics: RadrootsOrderEconomics { - quote_id: "quote-1".parse().expect("quote id"), - quote_version: 1, - pricing_basis: RadrootsOrderPricingBasis::ListingEvent, - currency: RadrootsCoreCurrency::USD, - items: vec![RadrootsOrderEconomicItem { - bin_id: "bin-1".parse().expect("bin id"), - bin_count: 2, - quantity_amount: RadrootsCoreDecimal::from(1u32), - quantity_unit: RadrootsCoreUnit::Each, - unit_price_amount: RadrootsCoreDecimal::from(5u32), - unit_price_currency: RadrootsCoreCurrency::USD, - line_subtotal: RadrootsCoreMoney::new( - RadrootsCoreDecimal::from(10u32), - RadrootsCoreCurrency::USD, - ), - }], - discounts: Vec::new(), - adjustments: Vec::new(), - subtotal: RadrootsCoreMoney::new( - RadrootsCoreDecimal::from(10u32), - RadrootsCoreCurrency::USD, - ), - discount_total: RadrootsCoreMoney::new( - RadrootsCoreDecimal::from(0u32), - RadrootsCoreCurrency::USD, - ), - adjustment_total: RadrootsCoreMoney::new( - RadrootsCoreDecimal::from(0u32), - RadrootsCoreCurrency::USD, - ), - total: RadrootsCoreMoney::new( - RadrootsCoreDecimal::from(10u32), - RadrootsCoreCurrency::USD, - ), - }, - } -} - -fn sample_order_decision() -> RadrootsOrderDecision { - let request = sample_order_request(); - - RadrootsOrderDecision { - order_id: request.order_id, - listing_addr: request.listing_addr, - buyer_pubkey: request.buyer_pubkey, - seller_pubkey: request.seller_pubkey, - decision: RadrootsOrderDecisionOutcome::Accepted { - inventory_commitments: vec![RadrootsOrderInventoryCommitment { - bin_id: "bin-1".parse().expect("bin id"), - bin_count: 2, - }], - }, - } -} - -fn sample_order_revision_proposal( - root_event_id: &RadrootsEventId, - prev_event_id: &RadrootsEventId, -) -> RadrootsOrderRevisionProposal { - let request = sample_order_request(); - - RadrootsOrderRevisionProposal { - revision_id: "revision-1".parse().expect("revision id"), - order_id: request.order_id, - listing_addr: request.listing_addr, - buyer_pubkey: request.buyer_pubkey, - seller_pubkey: request.seller_pubkey, - root_event_id: root_event_id.clone(), - prev_event_id: prev_event_id.clone(), - items: vec![RadrootsOrderItem { - bin_id: "bin-1".parse().expect("bin id"), - bin_count: 2, - }], - economics: request.economics, - reason: "more quantity".into(), - } -} - -fn sample_order_revision_decision( - proposal: &RadrootsOrderRevisionProposal, - prev_event_id: &RadrootsEventId, -) -> RadrootsOrderRevisionDecision { - RadrootsOrderRevisionDecision { - revision_id: proposal.revision_id.clone(), - order_id: proposal.order_id.clone(), - listing_addr: proposal.listing_addr.clone(), - buyer_pubkey: proposal.buyer_pubkey.clone(), - seller_pubkey: proposal.seller_pubkey.clone(), - root_event_id: proposal.root_event_id.clone(), - prev_event_id: prev_event_id.clone(), - decision: RadrootsOrderRevisionOutcome::Accepted, - } -} - -fn sample_order_cancellation() -> RadrootsOrderCancellation { - let request = sample_order_request(); - - RadrootsOrderCancellation { - order_id: request.order_id, - listing_addr: request.listing_addr, - buyer_pubkey: request.buyer_pubkey, - seller_pubkey: request.seller_pubkey, - reason: "buyer changed plan".into(), - } -} - -fn order_event_from_parts( - id_character: char, - author: String, - created_at: u32, - parts: WireEventParts, -) -> RadrootsNostrEvent { - RadrootsNostrEvent { - id: core::iter::repeat_n(id_character, 64).collect(), - author, - created_at, - kind: parts.kind, - tags: parts.tags, - content: parts.content, - sig: String::new(), - } -} - -#[test] -fn profile_build_draft_wraps_profile_encoder() { - let parts = - profile::build_draft(&sample_profile(), Some(RadrootsProfileType::Farm)).expect("profile"); - - assert_eq!(parts.kind, KIND_PROFILE); - assert!(parts.tags.iter().any(|tag| { - tag.first().map(|value| value.as_str()) == Some("t") - && tag.get(1).map(|value| value.as_str()) == Some("radroots:type:farm") - })); -} - -#[test] -fn farm_build_draft_wraps_farm_encoder() { - let parts = farm::build_draft(&sample_farm()).expect("farm"); - - assert_eq!(parts.kind, KIND_FARM); - assert!( - parts - .tags - .iter() - .any(|tag| tag.first().map(|value| value.as_str()) == Some("d")) - ); -} - -#[test] -fn listing_facade_wraps_build_parse_and_validate() { - let listing_value = sample_listing(); - let tags = listing::build_tags(&listing_value).expect("listing tags"); - assert!(!tags.is_empty()); - let parts = listing::build_draft(&listing_value).expect("listing draft"); - assert_eq!(parts.clone().into_wire_parts().kind, KIND_LISTING); - - let event = listing_event(&listing_value); - let parsed = listing::parse_event(&event).expect("parsed listing"); - assert_eq!(parsed.d_tag, listing_value.d_tag); - - let validated = order::validate_listing_event(&event).expect("validated listing"); - assert_eq!(validated.listing_id, listing_value.d_tag); - assert_eq!(event.kind, KIND_LISTING); -} - -#[test] -fn listing_parse_rejects_non_listing_kind() { - let listing_value = sample_listing(); - let mut event = listing_event(&listing_value); - event.kind = KIND_PROFILE; - - assert_eq!( - listing::parse_event(&event).expect_err("listing kind error"), - listing::RadrootsListingParseError::InvalidKind(KIND_PROFILE) - ); -} - -#[test] -fn order_facade_wraps_build_parse_and_address_ops() { - let listing_value = sample_listing(); - let seller_pubkey = "a".repeat(64); - let listing_addr = format!("{KIND_LISTING}:{seller_pubkey}:{}", listing_value.d_tag); - let payload = sample_order_request(); - let parts = - order::build_order_request_draft(&listing_event_ptr(), &payload).expect("order draft"); - - assert_eq!(parts.as_wire_parts().kind, KIND_ORDER_REQUEST); - assert_eq!(parts.clone().into_wire_parts().kind, KIND_ORDER_REQUEST); - - let parsed_addr = order::parse_listing_address(&listing_addr).expect("listing address"); - assert_eq!(parsed_addr, listing_addr); - - let event = order_event_from_parts( - 'b', - payload.buyer_pubkey.to_string(), - 2, - parts.into_wire_parts(), - ); - let envelope = order::parse_order_request(&event).expect("order envelope"); - assert_eq!(envelope.payload.order_id, payload.order_id); - assert_eq!(envelope.payload.listing_addr, listing_addr); - - let root_event_id = event_id('c'); - let previous_event_id = event_id('d'); - let decision = sample_order_decision(); - let decision_parts = - order::build_order_decision_draft(&root_event_id, &root_event_id, &decision) - .expect("decision draft"); - assert_eq!(decision_parts.as_wire_parts().kind, KIND_ORDER_DECISION); - assert_eq!( - decision_parts.clone().into_wire_parts().kind, - KIND_ORDER_DECISION - ); - let decision_event = order_event_from_parts( - 'e', - decision.seller_pubkey.to_string(), - 3, - decision_parts.into_wire_parts(), - ); - let decision_envelope = - order::parse_order_decision(&decision_event).expect("decision envelope"); - assert_eq!(decision_envelope.payload.order_id, decision.order_id); - - let proposal = sample_order_revision_proposal(&root_event_id, &previous_event_id); - let proposal_parts = - order::build_order_revision_proposal_draft(&root_event_id, &previous_event_id, &proposal) - .expect("proposal draft"); - assert_eq!( - proposal_parts.as_wire_parts().kind, - KIND_ORDER_REVISION_PROPOSAL - ); - assert_eq!( - proposal_parts.clone().into_wire_parts().kind, - KIND_ORDER_REVISION_PROPOSAL - ); - let proposal_event = order_event_from_parts( - 'f', - proposal.seller_pubkey.to_string(), - 4, - proposal_parts.into_wire_parts(), - ); - let proposal_envelope = - order::parse_order_revision_proposal(&proposal_event).expect("proposal envelope"); - assert_eq!(proposal_envelope.payload.revision_id, proposal.revision_id); - - let revision_decision = sample_order_revision_decision(&proposal, &previous_event_id); - let revision_decision_parts = order::build_order_revision_decision_draft( - &root_event_id, - &previous_event_id, - &revision_decision, - ) - .expect("revision decision draft"); - assert_eq!( - revision_decision_parts.as_wire_parts().kind, - KIND_ORDER_REVISION_DECISION - ); - assert_eq!( - revision_decision_parts.clone().into_wire_parts().kind, - KIND_ORDER_REVISION_DECISION - ); - let revision_decision_event = order_event_from_parts( - '0', - revision_decision.buyer_pubkey.to_string(), - 5, - revision_decision_parts.into_wire_parts(), - ); - let revision_decision_envelope = order::parse_order_revision_decision(&revision_decision_event) - .expect("revision decision envelope"); - assert_eq!( - revision_decision_envelope.payload.revision_id, - revision_decision.revision_id - ); - - let cancellation = sample_order_cancellation(); - let cancellation_parts = - order::build_order_cancellation_draft(&root_event_id, &previous_event_id, &cancellation) - .expect("cancellation draft"); - assert_eq!( - cancellation_parts.as_wire_parts().kind, - KIND_ORDER_CANCELLATION - ); - assert_eq!( - cancellation_parts.clone().into_wire_parts().kind, - KIND_ORDER_CANCELLATION - ); - let cancellation_event = order_event_from_parts( - '1', - cancellation.buyer_pubkey.to_string(), - 6, - cancellation_parts.into_wire_parts(), - ); - let cancellation_envelope = - order::parse_order_cancellation(&cancellation_event).expect("cancellation envelope"); - assert_eq!( - cancellation_envelope.payload.order_id, - cancellation.order_id - ); -} - -#[test] -fn order_facade_surfaces_order_draft_build_errors() { - let root_event_id = event_id('c'); - let previous_event_id = event_id('d'); - - let mut decision = sample_order_decision(); - decision.decision = RadrootsOrderDecisionOutcome::Accepted { - inventory_commitments: Vec::new(), - }; - assert!(order::build_order_decision_draft(&root_event_id, &root_event_id, &decision).is_err()); - - let mut proposal = sample_order_revision_proposal(&root_event_id, &previous_event_id); - proposal.reason = " ".into(); - assert!( - order::build_order_revision_proposal_draft(&root_event_id, &previous_event_id, &proposal) - .is_err() - ); - - let proposal = sample_order_revision_proposal(&root_event_id, &previous_event_id); - let mut revision_decision = sample_order_revision_decision(&proposal, &previous_event_id); - revision_decision.decision = RadrootsOrderRevisionOutcome::Declined { reason: " ".into() }; - assert!( - order::build_order_revision_decision_draft( - &root_event_id, - &previous_event_id, - &revision_decision - ) - .is_err() - ); - - let mut cancellation = sample_order_cancellation(); - cancellation.reason = " ".into(); - assert!( - order::build_order_cancellation_draft(&root_event_id, &previous_event_id, &cancellation) - .is_err() - ); -} diff --git a/crates/sdk/tests/orders_runtime.rs b/crates/sdk/tests/orders_runtime.rs @@ -9,24 +9,26 @@ use radroots_core::{ }; use radroots_event_store::{RadrootsEventIngest, RadrootsEventStore}; use radroots_events::{ - RadrootsNostrEvent, + RadrootsNostrEvent, RadrootsNostrEventPtr, contract::RadrootsActorRole, - ids::{RadrootsEventId, RadrootsOrderId, RadrootsOrderRevisionId, RadrootsPublicKey}, + ids::{ + RadrootsEventId, RadrootsListingAddress, RadrootsOrderId, RadrootsOrderRevisionId, + RadrootsPublicKey, + }, kinds::{KIND_LISTING, KIND_ORDER_DECISION, KIND_ORDER_REQUEST}, + order::{ + RadrootsOrderDecision, RadrootsOrderDecisionOutcome, RadrootsOrderEconomicItem, + RadrootsOrderEconomicLine, RadrootsOrderEconomics, RadrootsOrderInventoryCommitment, + RadrootsOrderItem, RadrootsOrderPricingBasis, RadrootsOrderRequest, + RadrootsOrderRevisionOutcome, + }, }; +use radroots_events_codec::wire::WireEventParts; use radroots_nostr::prelude::{ RadrootsNostrKeys, RadrootsNostrSecretKey, RadrootsNostrTimestamp, radroots_event_from_nostr, radroots_nostr_build_event, }; use radroots_outbox::RadrootsOutbox; -use radroots_sdk::protocol::events::RadrootsNostrEventPtr; -use radroots_sdk::protocol::order::{ - RadrootsListingAddress, RadrootsOrderDecision, RadrootsOrderDecisionOutcome, - RadrootsOrderEconomicItem, RadrootsOrderEconomicLine, RadrootsOrderEconomics, - RadrootsOrderInventoryCommitment, RadrootsOrderItem, RadrootsOrderPricingBasis, - RadrootsOrderRequest, RadrootsOrderRevisionOutcome, -}; -use radroots_sdk::protocol::wire::WireEventParts; use radroots_sdk::{ AckPolicy, PublishMode, RadrootsClient, RadrootsSdkError, RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, RelayResolutionPolicy, SdkRelayTargetSet, SdkRelayUrlPolicy, @@ -1784,8 +1786,8 @@ fn order_revision_proposal( raw_order_id: &str, root_event_id: &RadrootsEventId, previous_event_id: &RadrootsEventId, -) -> radroots_sdk::protocol::order::RadrootsOrderRevisionProposal { - radroots_sdk::protocol::order::RadrootsOrderRevisionProposal { +) -> RadrootsOrderRevisionProposal { + RadrootsOrderRevisionProposal { revision_id: format!("revision-{raw_order_id}") .parse() .expect("revision id"), @@ -1806,11 +1808,11 @@ fn order_revision_proposal( #[cfg(any())] fn order_revision_decision( - proposal: &radroots_sdk::protocol::order::RadrootsOrderRevisionProposal, + proposal: &RadrootsOrderRevisionProposal, previous_event_id: &RadrootsEventId, - decision: radroots_sdk::protocol::order::RadrootsOrderRevisionOutcome, -) -> radroots_sdk::protocol::order::RadrootsOrderRevisionDecision { - radroots_sdk::protocol::order::RadrootsOrderRevisionDecision { + decision: RadrootsOrderRevisionOutcome, +) -> RadrootsOrderRevisionDecision { + RadrootsOrderRevisionDecision { revision_id: proposal.revision_id.clone(), order_id: proposal.order_id.clone(), listing_addr: proposal.listing_addr.clone(), @@ -1823,10 +1825,8 @@ fn order_revision_decision( } #[cfg(any())] -fn order_cancellation( - raw_order_id: &str, -) -> radroots_sdk::protocol::order::RadrootsOrderCancellation { - radroots_sdk::protocol::order::RadrootsOrderCancellation { +fn order_cancellation(raw_order_id: &str) -> RadrootsOrderCancellation { + RadrootsOrderCancellation { order_id: order_id(raw_order_id), listing_addr: listing_address(), buyer_pubkey: BUYER_PUBLIC_KEY_HEX.parse().expect("buyer pubkey"), @@ -1875,12 +1875,12 @@ fn signed_event( } fn signed_order_request_event(raw_order_id: &str, created_at: u32) -> RadrootsNostrEvent { - let draft = radroots_sdk::protocol::order::build_order_request_draft( + let draft = radroots_events_codec::order::order_request_event_build( &listing_event_ptr(), &order_request(raw_order_id), ) .expect("request draft"); - signed_event(BUYER_SECRET_KEY_HEX, created_at, draft.into_wire_parts()) + signed_event(BUYER_SECRET_KEY_HEX, created_at, draft) } #[cfg(any())] @@ -1918,13 +1918,13 @@ fn signed_order_decision_event( root_event_id: &RadrootsEventId, created_at: u32, ) -> RadrootsNostrEvent { - let draft = radroots_sdk::protocol::order::build_order_decision_draft( + let draft = radroots_events_codec::order::order_decision_event_build( root_event_id, root_event_id, &order_decision(raw_order_id), ) .expect("decision draft"); - signed_event(SELLER_SECRET_KEY_HEX, created_at, draft.into_wire_parts()) + signed_event(SELLER_SECRET_KEY_HEX, created_at, draft) } fn signed_non_order_event(created_at: u32) -> RadrootsNostrEvent { diff --git a/crates/sdk/tests/replica_ingest.rs b/crates/sdk/tests/replica_ingest.rs @@ -1,8 +1,7 @@ +use radroots_events::{RadrootsNostrEvent, farm::RadrootsFarm}; use radroots_replica_db::ReplicaSql; use radroots_replica_db_schema::farm::IFarmFindMany; use radroots_replica_sync::{RadrootsReplicaIngestOutcome, radroots_replica_ingest_event}; -use radroots_sdk::protocol::events::RadrootsNostrEvent; -use radroots_sdk::protocol::farm::{self, RadrootsFarm}; use radroots_sql_core::{SqlExecutor, SqliteExecutor}; use tempfile::{TempDir, tempdir}; @@ -57,7 +56,8 @@ fn open_replica() -> (TempDir, ReplicaSql<SqliteExecutor>) { fn ingest_farm(replica: &ReplicaSql<SqliteExecutor>) -> RadrootsNostrEvent { let farm_value = sample_farm(); let author = seller_pubkey(); - let parts = farm::build_draft(&farm_value).expect("farm draft"); + let parts = + radroots_events_codec::farm::encode::to_wire_parts(&farm_value).expect("farm draft"); let event = sdk_event( 1, &author, diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -211,7 +211,7 @@ const FORBIDDEN_ORDER_RUNTIME_PUBLIC_EXPORTS: &[&str] = &[ "ORDER_SETTLEMENT_DECISION_OPERATION_KIND", ]; -const FORBIDDEN_LEGACY_ORDER_PRODUCT_EXPORTS: &[&str] = &[ +const FORBIDDEN_REMOVED_ORDER_PRODUCT_EXPORTS: &[&str] = &[ "ORDER_CANCELLATION_OPERATION_KIND", "ORDER_DECISION_OPERATION_KIND", "ORDER_REVISION_DECISION_OPERATION_KIND", @@ -408,10 +408,10 @@ fn order_runtime_public_exports_are_explicit() { ); } - for forbidden in FORBIDDEN_LEGACY_ORDER_PRODUCT_EXPORTS { + for forbidden in FORBIDDEN_REMOVED_ORDER_PRODUCT_EXPORTS { assert!( !source.contains(forbidden), - "src/lib.rs must not expose legacy order SDK product export `{forbidden}`" + "src/lib.rs must not expose removed order SDK product export `{forbidden}`" ); } @@ -600,7 +600,7 @@ fn product_clients_remain_thin_sdk_handles() { } #[test] -fn legacy_client_and_config_modules_are_removed() { +fn removed_client_and_config_modules_are_absent() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); for relative_path in ["src/client.rs", "src/config.rs"] { @@ -613,7 +613,7 @@ fn legacy_client_and_config_modules_are_removed() { } #[test] -fn legacy_trade_client_root_export_is_removed() { +fn removed_trade_client_root_export_is_absent() { let source = read_source( Path::new(env!("CARGO_MANIFEST_DIR")) .join("src/lib.rs") @@ -622,12 +622,12 @@ fn legacy_trade_client_root_export_is_removed() { assert!( !source.contains("TradeClient"), - "src/lib.rs must not re-export the legacy TradeClient facade" + "src/lib.rs must not re-export the removed TradeClient facade" ); } #[test] -fn legacy_client_config_modules_are_not_public() { +fn removed_client_config_modules_are_not_public() { let source = read_source( Path::new(env!("CARGO_MANIFEST_DIR")) .join("src/lib.rs") @@ -650,11 +650,29 @@ fn legacy_client_config_modules_are_not_public() { ] { assert!( !source.contains(forbidden), - "src/lib.rs must not expose legacy SDK client/config concept `{forbidden}`" + "src/lib.rs must not expose removed SDK client/config concept `{forbidden}`" ); } } +#[test] +fn sdk_public_api_does_not_export_protocol_workflow_bypass() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let source = read_source(manifest_dir.join("src/lib.rs").as_path()); + + for forbidden in ["pub mod protocol;", "pub use crate::protocol"] { + assert!( + !source.contains(forbidden), + "src/lib.rs must not expose protocol workflow bypass `{forbidden}`" + ); + } + + assert!( + !manifest_dir.join("src/protocol").exists(), + "src/protocol must not remain as a public protocol re-export surface" + ); +} + fn product_runtime_file_stays_on_boundary(relative_path: &str) { let source = read_source( Path::new(env!("CARGO_MANIFEST_DIR")) @@ -675,7 +693,7 @@ fn product_runtime_file_stays_on_boundary(relative_path: &str) { ] { assert!( !source.contains(forbidden), - "{relative_path} must not use legacy SDK client or transport concept `{forbidden}`" + "{relative_path} must not use removed SDK client or transport concept `{forbidden}`" ); } } diff --git a/crates/sdk/tests/trade_product_publish_runtime.rs b/crates/sdk/tests/trade_product_publish_runtime.rs @@ -9,13 +9,17 @@ use radroots_authority::RadrootsActorContext; use radroots_core::{ RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreUnit, }; -use radroots_events::{contract::RadrootsActorRole, kinds::KIND_LISTING}; -use radroots_nostr::prelude::{RadrootsNostrKeys, RadrootsNostrSecretKey}; -use radroots_sdk::protocol::events::RadrootsNostrEventPtr; -use radroots_sdk::protocol::order::{ - RadrootsListingAddress, RadrootsOrderEconomicItem, RadrootsOrderEconomicLine, - RadrootsOrderEconomics, RadrootsOrderItem, RadrootsOrderPricingBasis, RadrootsOrderRequest, +use radroots_events::{ + RadrootsNostrEventPtr, + contract::RadrootsActorRole, + ids::RadrootsListingAddress, + kinds::KIND_LISTING, + order::{ + RadrootsOrderEconomicItem, RadrootsOrderEconomicLine, RadrootsOrderEconomics, + RadrootsOrderItem, RadrootsOrderPricingBasis, RadrootsOrderRequest, + }, }; +use radroots_nostr::prelude::{RadrootsNostrKeys, RadrootsNostrSecretKey}; use radroots_sdk::{ AckPolicy, PublishMode, PushOutboxRelayOutcomeKind, RadrootsClient, RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, RelayResolutionPolicy, SdkPublishTransport,