lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 4947fcc9f28627ae85711429b3bb1a6ee114127d
parent 6eb503f43415039b5c70bbd97e0f64f6cfe19329
Author: triesap <tyson@radroots.org>
Date:   Sat, 13 Jun 2026 04:36:49 -0700

events: type order protocol identifiers

- type order payload pubkeys and chain event references with validated id newtypes
- tighten order codec builders and decode context around typed event ids and pubkeys
- align SDK order helpers and deterministic test fixtures with canonical wire strings
- validate cargo fmt, events, codec, trade, SDK, and order golden lanes

Diffstat:
Mcrates/events/src/order.rs | 192+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mcrates/events_codec/src/order/decode.rs | 374+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mcrates/events_codec/src/order/encode.rs | 57+++++++++++++++++++++++++++++----------------------------
Mcrates/sdk/src/client.rs | 49+++++++++++++++++++++++++------------------------
Mcrates/sdk/src/order.rs | 25+++++++++++++------------
Mcrates/sdk/tests/client.rs | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mcrates/sdk/tests/facade.rs | 20++++++++++++++------
Mcrates/sdk/tests/radrootsd.rs | 13+++++++------
Mcrates/sdk/tests/relay_direct.rs | 111++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mcrates/trade/src/order.rs | 56++++++++++++++++++++------------------------------------
10 files changed, 587 insertions(+), 414 deletions(-)

diff --git a/crates/events/src/order.rs b/crates/events/src/order.rs @@ -7,8 +7,8 @@ use alloc::{ }; use crate::ids::{ - RadrootsEconomicsDigest, RadrootsInventoryBinId, RadrootsListingAddress, RadrootsOrderId, - RadrootsOrderQuoteId, RadrootsOrderRevisionId, + RadrootsEconomicsDigest, RadrootsEventId, RadrootsInventoryBinId, RadrootsListingAddress, + RadrootsOrderId, RadrootsOrderQuoteId, RadrootsOrderRevisionId, RadrootsPublicKey, }; use crate::kinds::*; pub use crate::order_economics::*; @@ -164,8 +164,8 @@ impl RadrootsOrderEconomics { pub struct RadrootsOrderRequest { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, pub items: Vec<RadrootsOrderItem>, pub economics: RadrootsOrderEconomics, } @@ -188,10 +188,10 @@ pub struct RadrootsOrderRevisionProposal { pub revision_id: RadrootsOrderRevisionId, pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub items: Vec<RadrootsOrderItem>, pub economics: RadrootsOrderEconomics, pub reason: String, @@ -236,10 +236,10 @@ pub struct RadrootsOrderRevisionDecision { pub revision_id: RadrootsOrderRevisionId, pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub decision: RadrootsOrderRevisionOutcome, } @@ -291,8 +291,8 @@ impl RadrootsOrderDecisionOutcome { pub struct RadrootsOrderDecision { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, pub decision: RadrootsOrderDecisionOutcome, } @@ -330,8 +330,8 @@ impl RadrootsOrderFulfillmentState { pub struct RadrootsOrderFulfillmentUpdate { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, pub status: RadrootsOrderFulfillmentState, } @@ -354,8 +354,8 @@ impl RadrootsOrderFulfillmentUpdate { pub struct RadrootsOrderCancellation { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, pub reason: String, } @@ -374,8 +374,8 @@ impl RadrootsOrderCancellation { pub struct RadrootsOrderReceipt { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, pub received: bool, pub issue: Option<String>, pub received_at: u64, @@ -415,11 +415,11 @@ pub enum RadrootsOrderPaymentMethod { pub struct RadrootsOrderPaymentRecord { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub buyer_pubkey: String, - pub seller_pubkey: String, - pub root_event_id: String, - pub previous_event_id: String, - pub agreement_event_id: String, + pub buyer_pubkey: RadrootsPublicKey, + pub seller_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub previous_event_id: RadrootsEventId, + pub agreement_event_id: RadrootsEventId, pub quote_id: RadrootsOrderQuoteId, pub quote_version: u32, pub economics_digest: RadrootsEconomicsDigest, @@ -467,12 +467,12 @@ pub enum RadrootsOrderSettlementOutcome { pub struct RadrootsOrderSettlementDecision { pub order_id: RadrootsOrderId, pub listing_addr: RadrootsListingAddress, - pub seller_pubkey: String, - pub buyer_pubkey: String, - pub root_event_id: String, - pub previous_event_id: String, - pub agreement_event_id: String, - pub payment_event_id: String, + pub seller_pubkey: RadrootsPublicKey, + pub buyer_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub previous_event_id: RadrootsEventId, + pub agreement_event_id: RadrootsEventId, + pub payment_event_id: RadrootsEventId, pub quote_id: RadrootsOrderQuoteId, pub quote_version: u32, pub economics_digest: RadrootsEconomicsDigest, @@ -1065,12 +1065,30 @@ mod tests { RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreUnit, }; - fn sample_pubkey() -> String { - "0".repeat(64) + fn pubkey(character: char) -> RadrootsPublicKey { + core::iter::repeat_n(character, 64) + .collect::<String>() + .parse() + .unwrap() + } + + fn event_id(character: char) -> RadrootsEventId { + core::iter::repeat_n(character, 64) + .collect::<String>() + .parse() + .unwrap() + } + + fn buyer_pubkey() -> RadrootsPublicKey { + pubkey('b') + } + + fn seller_pubkey() -> RadrootsPublicKey { + pubkey('a') } fn sample_listing_addr() -> RadrootsListingAddress { - format!("30402:{}:AAAAAAAAAAAAAAAAAAAAAg", sample_pubkey()) + format!("30402:{}:AAAAAAAAAAAAAAAAAAAAAg", seller_pubkey()) .parse() .unwrap() } @@ -1099,8 +1117,8 @@ mod tests { RadrootsOrderRequest { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), items: vec![RadrootsOrderItem { bin_id: bin_id("bin-1"), bin_count: 2, @@ -1211,8 +1229,8 @@ mod tests { RadrootsOrderDecision { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), decision: RadrootsOrderDecisionOutcome::Accepted { inventory_commitments: vec![sample_inventory_commitment()], }, @@ -1223,8 +1241,8 @@ mod tests { RadrootsOrderFulfillmentUpdate { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), status: RadrootsOrderFulfillmentState::ReadyForPickup, } } @@ -1233,8 +1251,8 @@ mod tests { RadrootsOrderCancellation { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), reason: "changed plans".into(), } } @@ -1243,8 +1261,8 @@ mod tests { RadrootsOrderReceipt { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), received, issue: (!received).then(|| "damaged items".into()), received_at: 1_777_665_600, @@ -1256,10 +1274,10 @@ mod tests { revision_id: revision_id("rev-1"), order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), - root_event_id: "root-event".into(), - prev_event_id: "previous-event".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), + root_event_id: event_id('1'), + prev_event_id: event_id('2'), items: vec![RadrootsOrderItem { bin_id: bin_id("bin-1"), bin_count: 2, @@ -1276,10 +1294,10 @@ mod tests { revision_id: revision_id("rev-1"), order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), - root_event_id: "root-event".into(), - prev_event_id: "previous-event".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), + root_event_id: event_id('1'), + prev_event_id: event_id('2'), decision, } } @@ -1288,11 +1306,11 @@ mod tests { RadrootsOrderPaymentRecord { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), - root_event_id: "root-event".into(), - previous_event_id: "previous-event".into(), - agreement_event_id: "agreement-event".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), + root_event_id: event_id('1'), + previous_event_id: event_id('2'), + agreement_event_id: event_id('3'), quote_id: quote_id("quote-1"), quote_version: 1, economics_digest: digest("economics-digest"), @@ -1311,12 +1329,12 @@ mod tests { RadrootsOrderSettlementDecision { order_id: order_id("order-1"), listing_addr: sample_listing_addr(), - seller_pubkey: "seller".into(), - buyer_pubkey: "buyer".into(), - root_event_id: "root-event".into(), - previous_event_id: "previous-event".into(), - agreement_event_id: "agreement-event".into(), - payment_event_id: "payment-event".into(), + seller_pubkey: seller_pubkey(), + buyer_pubkey: buyer_pubkey(), + root_event_id: event_id('1'), + previous_event_id: event_id('2'), + agreement_event_id: event_id('3'), + payment_event_id: event_id('4'), quote_id: quote_id("quote-1"), quote_version: 1, economics_digest: digest("economics-digest"), @@ -1491,13 +1509,6 @@ mod tests { fn order_request_validation_rejects_invalid_fields() { assert_eq!(sample_order_request().validate(), Ok(())); - let mut missing_buyer_pubkey = sample_order_request(); - missing_buyer_pubkey.buyer_pubkey = " ".into(); - assert_eq!( - missing_buyer_pubkey.validate().unwrap_err(), - RadrootsOrderPayloadError::EmptyField("buyer_pubkey") - ); - let mut missing_items = sample_order_request(); missing_items.items.clear(); assert_eq!( @@ -1535,6 +1546,29 @@ mod tests { } #[test] + fn order_payload_json_rejects_invalid_protocol_identifiers() { + let mut request = serde_json::to_value(sample_order_request()).unwrap(); + request["buyer_pubkey"] = serde_json::json!("not-a-pubkey"); + assert!(serde_json::from_value::<RadrootsOrderRequest>(request).is_err()); + + let mut revision = serde_json::to_value(sample_order_revision_proposal()).unwrap(); + revision["root_event_id"] = serde_json::json!("not-an-event-id"); + assert!(serde_json::from_value::<RadrootsOrderRevisionProposal>(revision).is_err()); + + let mut payment = serde_json::to_value(sample_payment_recorded()).unwrap(); + payment["agreement_event_id"] = serde_json::json!("not-an-event-id"); + assert!(serde_json::from_value::<RadrootsOrderPaymentRecord>(payment).is_err()); + + let mut settlement = serde_json::to_value(sample_settlement_decision( + RadrootsOrderSettlementOutcome::Accepted, + None, + )) + .unwrap(); + settlement["payment_event_id"] = serde_json::json!("not-an-event-id"); + assert!(serde_json::from_value::<RadrootsOrderSettlementDecision>(settlement).is_err()); + } + + #[test] fn order_economics_validation_accepts_canonical_totals() { let economics = sample_order_economics(); assert_eq!(economics.validate(), Ok(())); @@ -1962,15 +1996,6 @@ mod tests { derived.validate().unwrap_err(), RadrootsOrderPayloadError::InvalidFulfillmentStatus ); - - let missing_seller = RadrootsOrderFulfillmentUpdate { - seller_pubkey: " ".into(), - ..sample_order_fulfillment_update() - }; - assert_eq!( - missing_seller.validate().unwrap_err(), - RadrootsOrderPayloadError::EmptyField("seller_pubkey") - ); } #[test] @@ -1985,15 +2010,6 @@ mod tests { missing_reason.validate().unwrap_err(), RadrootsOrderPayloadError::EmptyField("reason") ); - - let missing_buyer = RadrootsOrderCancellation { - buyer_pubkey: " ".into(), - ..sample_order_cancellation() - }; - assert_eq!( - missing_buyer.validate().unwrap_err(), - RadrootsOrderPayloadError::EmptyField("buyer_pubkey") - ); } #[test] diff --git a/crates/events_codec/src/order/decode.rs b/crates/events_codec/src/order/decode.rs @@ -4,6 +4,7 @@ use alloc::{borrow::ToOwned, format, string::String, vec::Vec}; #[cfg(feature = "serde_json")] use radroots_events::{ RadrootsNostrEvent, RadrootsNostrEventPtr, + ids::{RadrootsEventId, RadrootsPublicKey}, kinds::{KIND_PROFILE, is_order_event_kind}, order::{ RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderEnvelope, @@ -96,10 +97,10 @@ impl std::error::Error for RadrootsOrderEnvelopeParseError { #[cfg(feature = "serde_json")] #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderEventContext { - pub counterparty_pubkey: String, + pub counterparty_pubkey: RadrootsPublicKey, pub listing_event: Option<RadrootsNostrEventPtr>, - pub root_event_id: Option<String>, - pub prev_event_id: Option<String>, + pub root_event_id: Option<RadrootsEventId>, + pub prev_event_id: Option<RadrootsEventId>, } #[cfg(feature = "serde_json")] @@ -487,12 +488,26 @@ pub fn order_event_context_from_tags( ) -> Result<RadrootsOrderEventContext, RadrootsOrderEnvelopeParseError> { let counterparty_pubkey = parse_order_counterparty_tag(tags).map_err(map_tag_parse_error_for_order_envelope)?; + let counterparty_pubkey = RadrootsPublicKey::parse(&counterparty_pubkey) + .map_err(|_| RadrootsOrderEnvelopeParseError::InvalidTag("p"))?; let listing_event = parse_order_listing_event_tag(tags).map_err(map_tag_parse_error_for_order_envelope)?; let root_event_id = parse_order_root_tag(tags).map_err(map_tag_parse_error_for_order_envelope)?; + let root_event_id = root_event_id + .map(|id| { + RadrootsEventId::parse(id) + .map_err(|_| RadrootsOrderEnvelopeParseError::InvalidTag(TAG_E_ROOT)) + }) + .transpose()?; let prev_event_id = parse_order_prev_tag(tags).map_err(map_tag_parse_error_for_order_envelope)?; + let prev_event_id = prev_event_id + .map(|id| { + RadrootsEventId::parse(id) + .map_err(|_| RadrootsOrderEnvelopeParseError::InvalidTag(TAG_E_PREV)) + }) + .transpose()?; if message_type.requires_listing_snapshot() && listing_event.is_none() { return Err(RadrootsOrderEnvelopeParseError::MissingTag( @@ -578,9 +593,8 @@ fn validate_order_binding<T>( if event.author != expected_author { return Err(RadrootsOrderEnvelopeParseError::AuthorMismatch); } - let counterparty = parse_order_counterparty_tag(&event.tags) - .map_err(map_tag_parse_error_for_order_envelope)?; - if counterparty != expected_counterparty { + let context = order_event_context_from_tags(envelope.message_type, &event.tags)?; + if context.counterparty_pubkey.as_str() != expected_counterparty { return Err(RadrootsOrderEnvelopeParseError::CounterpartyTagMismatch); } Ok(()) @@ -608,8 +622,9 @@ mod tests { use radroots_events::{ RadrootsNostrEvent, RadrootsNostrEventPtr, ids::{ - RadrootsEconomicsDigest, RadrootsInventoryBinId, RadrootsListingAddress, - RadrootsOrderId, RadrootsOrderQuoteId, RadrootsOrderRevisionId, + RadrootsEconomicsDigest, RadrootsEventId, RadrootsInventoryBinId, + RadrootsListingAddress, RadrootsOrderId, RadrootsOrderQuoteId, RadrootsOrderRevisionId, + RadrootsPublicKey, }, kinds::{ KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_FULFILLMENT_UPDATE, @@ -631,12 +646,31 @@ mod tests { tags::{TAG_D, TAG_E_PREV, TAG_E_ROOT}, }; - fn seller_pubkey() -> String { - "a".repeat(64) + fn pubkey(character: char) -> RadrootsPublicKey { + core::iter::repeat_n(character, 64) + .collect::<String>() + .parse() + .unwrap() + } + + fn buyer_pubkey() -> RadrootsPublicKey { + pubkey('b') + } + + fn seller_pubkey() -> RadrootsPublicKey { + pubkey('a') + } + + fn buyer_pubkey_wire() -> String { + buyer_pubkey().into_string() + } + + fn seller_pubkey_wire() -> String { + seller_pubkey().into_string() } fn listing_addr() -> RadrootsListingAddress { - format!("30402:{}:AAAAAAAAAAAAAAAAAAAAAg", seller_pubkey()) + format!("30402:{}:AAAAAAAAAAAAAAAAAAAAAg", seller_pubkey_wire()) .parse() .unwrap() } @@ -665,12 +699,23 @@ mod tests { raw.parse().unwrap() } + fn event_id(character: char) -> RadrootsEventId { + core::iter::repeat_n(character, 64) + .collect::<String>() + .parse() + .unwrap() + } + + fn event_id_wire(character: char) -> String { + event_id(character).into_string() + } + fn order_request() -> RadrootsOrderRequest { RadrootsOrderRequest { order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), items: vec![RadrootsOrderItem { bin_id: bin_id("lb"), bin_count: 3, @@ -715,8 +760,8 @@ mod tests { RadrootsOrderDecision { order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), decision: RadrootsOrderDecisionOutcome::Accepted { inventory_commitments: vec![RadrootsOrderInventoryCommitment { bin_id: bin_id("lb"), @@ -739,10 +784,10 @@ mod tests { revision_id: revision_id("rev-1"), order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), - root_event_id: "root-event".into(), - prev_event_id: "decision-event".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), + root_event_id: event_id('1'), + prev_event_id: event_id('2'), items: vec![RadrootsOrderItem { bin_id: bin_id("lb"), bin_count: 4, @@ -759,10 +804,10 @@ mod tests { revision_id: revision_id("rev-1"), order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), - root_event_id: "root-event".into(), - prev_event_id: "revision-event".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), + root_event_id: event_id('1'), + prev_event_id: event_id('3'), decision, } } @@ -771,8 +816,8 @@ mod tests { RadrootsOrderFulfillmentUpdate { order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), status: RadrootsOrderFulfillmentState::ReadyForPickup, } } @@ -781,8 +826,8 @@ mod tests { RadrootsOrderCancellation { order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), reason: "changed plans".into(), } } @@ -791,8 +836,8 @@ mod tests { RadrootsOrderReceipt { order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), received, issue: (!received).then(|| "damaged items".into()), received_at: 1_777_665_600, @@ -803,11 +848,11 @@ mod tests { RadrootsOrderPaymentRecord { order_id: order_id("order-1"), listing_addr: listing_addr(), - buyer_pubkey: "buyer".into(), - seller_pubkey: "seller".into(), - root_event_id: "root-event".into(), - previous_event_id: "agreement-event".into(), - agreement_event_id: "agreement-event".into(), + buyer_pubkey: buyer_pubkey(), + seller_pubkey: seller_pubkey(), + root_event_id: event_id('1'), + previous_event_id: event_id('4'), + agreement_event_id: event_id('4'), quote_id: quote_id("quote-1"), quote_version: 1, economics_digest: digest("digest-1"), @@ -825,12 +870,12 @@ mod tests { RadrootsOrderSettlementDecision { order_id: order_id("order-1"), listing_addr: listing_addr(), - seller_pubkey: "seller".into(), - buyer_pubkey: "buyer".into(), - root_event_id: "root-event".into(), - previous_event_id: "payment-event".into(), - agreement_event_id: "agreement-event".into(), - payment_event_id: "payment-event".into(), + seller_pubkey: seller_pubkey(), + buyer_pubkey: buyer_pubkey(), + root_event_id: event_id('1'), + previous_event_id: event_id('5'), + agreement_event_id: event_id('4'), + payment_event_id: event_id('5'), quote_id: quote_id("quote-1"), quote_version: 1, economics_digest: digest("digest-1"), @@ -842,22 +887,18 @@ mod tests { } } - fn event_id(character: char) -> String { - core::iter::repeat_n(character, 64).collect() - } - fn listing_event_ptr() -> RadrootsNostrEventPtr { RadrootsNostrEventPtr { - id: event_id('a'), + id: event_id_wire('a'), relays: Some("wss://relay.example.com".into()), } } #[test] fn listing_address_roundtrips() { - let addr = RadrootsOrderListingAddress::parse("30402:seller:AAAAAAAAAAAAAAAAAAAAAg") - .expect("parse listing address"); - assert_eq!(addr.as_str(), "30402:seller:AAAAAAAAAAAAAAAAAAAAAg"); + let raw = format!("30402:{}:AAAAAAAAAAAAAAAAAAAAAg", seller_pubkey_wire()); + let addr = RadrootsOrderListingAddress::parse(&raw).expect("parse listing address"); + assert_eq!(addr.as_str(), raw); } #[test] @@ -873,7 +914,7 @@ mod tests { RadrootsOrderEventType::OrderRequested ); assert_eq!(envelope.order_id, "order-1"); - assert_eq!(built.tags[0], vec!["p".to_string(), "seller".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), seller_pubkey_wire()]); assert_eq!(built.tags[1], vec!["a".to_string(), listing_addr_wire()]); assert_eq!( built.tags[2], @@ -898,13 +939,15 @@ mod tests { #[test] fn order_decision_builder_emits_canonical_chain_shape() { let payload = order_decision(); - let built = order_decision_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = order_decision_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let envelope: RadrootsOrderEnvelope<RadrootsOrderDecision> = serde_json::from_str(&built.content).unwrap(); assert_eq!(built.kind, KIND_ORDER_DECISION); assert_eq!(envelope.message_type, RadrootsOrderEventType::OrderDecision); - assert_eq!(built.tags[0], vec!["p".to_string(), "buyer".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), buyer_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -913,13 +956,13 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "prev-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('9')]) ); } @@ -927,8 +970,8 @@ mod tests { fn order_revision_proposal_builder_emits_canonical_chain_shape() { let payload = order_revision_proposal(); let built = order_revision_proposal_event_build( - payload.root_event_id.as_str(), - payload.prev_event_id.as_str(), + &payload.root_event_id, + &payload.prev_event_id, &payload, ) .unwrap(); @@ -940,7 +983,7 @@ mod tests { envelope.message_type, RadrootsOrderEventType::OrderRevisionProposed ); - assert_eq!(built.tags[0], vec!["p".to_string(), "buyer".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), buyer_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -951,13 +994,13 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "decision-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('2')]) ); } @@ -965,8 +1008,8 @@ mod tests { fn order_revision_decision_builder_emits_canonical_chain_shape() { let payload = order_revision_decision(RadrootsOrderRevisionOutcome::Accepted); let built = order_revision_decision_event_build( - payload.root_event_id.as_str(), - payload.prev_event_id.as_str(), + &payload.root_event_id, + &payload.prev_event_id, &payload, ) .unwrap(); @@ -978,7 +1021,7 @@ mod tests { envelope.message_type, RadrootsOrderEventType::OrderRevisionDecision ); - assert_eq!(built.tags[0], vec!["p".to_string(), "seller".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), seller_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -988,21 +1031,23 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "revision-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('3')]) ); } #[test] fn order_fulfillment_update_builder_emits_canonical_chain_shape() { let payload = order_fulfillment_update(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); let built = - order_fulfillment_update_event_build("root-event", "prev-event", &payload).unwrap(); + order_fulfillment_update_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let envelope: RadrootsOrderEnvelope<RadrootsOrderFulfillmentUpdate> = serde_json::from_str(&built.content).unwrap(); @@ -1012,7 +1057,7 @@ mod tests { RadrootsOrderEventType::FulfillmentUpdated ); assert_eq!(envelope.payload.status, payload.status); - assert_eq!(built.tags[0], vec!["p".to_string(), "buyer".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), buyer_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -1021,20 +1066,23 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "prev-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('9')]) ); } #[test] fn order_cancellation_builder_emits_canonical_buyer_chain_shape() { let payload = order_cancelled(); - let built = order_cancellation_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = + order_cancellation_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let envelope: RadrootsOrderEnvelope<RadrootsOrderCancellation> = serde_json::from_str(&built.content).unwrap(); @@ -1044,7 +1092,7 @@ mod tests { RadrootsOrderEventType::OrderCancelled ); assert_eq!(envelope.payload.reason, payload.reason); - assert_eq!(built.tags[0], vec!["p".to_string(), "seller".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), seller_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -1053,20 +1101,22 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "prev-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('9')]) ); } #[test] fn order_buyer_receipt_builder_emits_canonical_buyer_chain_shape() { let payload = order_buyer_receipt(false); - let built = order_receipt_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = order_receipt_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let envelope: RadrootsOrderEnvelope<RadrootsOrderReceipt> = serde_json::from_str(&built.content).unwrap(); @@ -1074,7 +1124,7 @@ mod tests { assert_eq!(envelope.message_type, RadrootsOrderEventType::BuyerReceipt); assert_eq!(envelope.payload.received, false); assert_eq!(envelope.payload.issue.as_deref(), Some("damaged items")); - assert_eq!(built.tags[0], vec!["p".to_string(), "seller".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), seller_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -1083,13 +1133,13 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "prev-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('9')]) ); } @@ -1097,8 +1147,8 @@ mod tests { fn order_payment_recorded_builder_emits_canonical_buyer_chain_shape() { let payload = order_payment_recorded(); let built = order_payment_record_event_build( - payload.root_event_id.as_str(), - payload.previous_event_id.as_str(), + &payload.root_event_id, + &payload.previous_event_id, &payload, ) .unwrap(); @@ -1112,7 +1162,7 @@ mod tests { ); assert_eq!(envelope.payload.amount, decimal("15")); assert_eq!(envelope.payload.method, RadrootsOrderPaymentMethod::Cash); - assert_eq!(built.tags[0], vec!["p".to_string(), "seller".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), seller_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -1121,13 +1171,13 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "agreement-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('4')]) ); } @@ -1135,8 +1185,8 @@ mod tests { fn order_settlement_decision_builder_emits_canonical_seller_chain_shape() { let payload = order_settlement_decision(RadrootsOrderSettlementOutcome::Accepted); let built = order_settlement_decision_event_build( - payload.root_event_id.as_str(), - payload.previous_event_id.as_str(), + &payload.root_event_id, + &payload.previous_event_id, &payload, ) .unwrap(); @@ -1153,7 +1203,7 @@ mod tests { RadrootsOrderSettlementOutcome::Accepted ); assert_eq!(envelope.payload.reason, None); - assert_eq!(built.tags[0], vec!["p".to_string(), "buyer".to_string()]); + assert_eq!(built.tags[0], vec!["p".to_string(), buyer_pubkey_wire()]); assert_eq!( built.tags[2], vec![TAG_D.to_string(), "order-1".to_string()] @@ -1162,13 +1212,13 @@ mod tests { built .tags .iter() - .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), "root-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_ROOT.to_string(), event_id_wire('1')]) ); assert!( built .tags .iter() - .any(|tag| tag == &vec![TAG_E_PREV.to_string(), "payment-event".to_string()]) + .any(|tag| tag == &vec![TAG_E_PREV.to_string(), event_id_wire('5')]) ); } @@ -1177,8 +1227,8 @@ mod tests { let payload = order_request(); let built = order_request_event_build(&listing_event_ptr(), &payload).unwrap(); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "buyer".into(), + id: event_id_wire('e'), + author: buyer_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1206,8 +1256,8 @@ mod tests { payload, ); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "buyer".into(), + id: event_id_wire('e'), + author: buyer_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1228,10 +1278,12 @@ mod tests { #[test] fn order_decision_parse_roundtrips_and_validates_chain_tags() { let payload = order_decision(); - let built = order_decision_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = order_decision_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1247,11 +1299,13 @@ mod tests { #[test] fn order_fulfillment_update_parse_roundtrips_and_validates_chain_tags() { let payload = order_fulfillment_update(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); let built = - order_fulfillment_update_event_build("root-event", "prev-event", &payload).unwrap(); + order_fulfillment_update_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1270,10 +1324,13 @@ mod tests { #[test] fn order_cancellation_parse_roundtrips_and_validates_buyer_actor() { let payload = order_cancelled(); - let built = order_cancellation_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = + order_cancellation_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "buyer".into(), + id: event_id_wire('e'), + author: buyer_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1292,10 +1349,12 @@ mod tests { #[test] fn order_buyer_receipt_parse_roundtrips_and_validates_buyer_actor() { let payload = order_buyer_receipt(true); - let built = order_receipt_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = order_receipt_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "buyer".into(), + id: event_id_wire('e'), + author: buyer_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1312,14 +1371,14 @@ mod tests { fn order_payment_recorded_parse_roundtrips_and_validates_buyer_actor() { let payload = order_payment_recorded(); let built = order_payment_record_event_build( - payload.root_event_id.as_str(), - payload.previous_event_id.as_str(), + &payload.root_event_id, + &payload.previous_event_id, &payload, ) .unwrap(); let event = RadrootsNostrEvent { - id: "payment-event".into(), - author: "buyer".into(), + id: event_id_wire('f'), + author: buyer_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1339,14 +1398,14 @@ mod tests { fn order_settlement_decision_parse_roundtrips_and_validates_seller_actor() { let payload = order_settlement_decision(RadrootsOrderSettlementOutcome::Rejected); let built = order_settlement_decision_event_build( - payload.root_event_id.as_str(), - payload.previous_event_id.as_str(), + &payload.root_event_id, + &payload.previous_event_id, &payload, ) .unwrap(); let event = RadrootsNostrEvent { - id: "settlement-event".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1366,14 +1425,14 @@ mod tests { fn order_revision_proposal_parse_validates_actor_counterparty_and_chain_payload() { let payload = order_revision_proposal(); let built = order_revision_proposal_event_build( - payload.root_event_id.as_str(), - payload.prev_event_id.as_str(), + &payload.root_event_id, + &payload.prev_event_id, &payload, ) .unwrap(); let mut event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1383,7 +1442,7 @@ mod tests { let envelope = order_revision_proposal_from_event(&event).unwrap(); assert_eq!(envelope.payload, payload); - event.author = "buyer".into(); + event.author = buyer_pubkey_wire(); let err = order_revision_proposal_from_event(&event).unwrap_err(); assert_eq!(err, RadrootsOrderEnvelopeParseError::AuthorMismatch); } @@ -1394,14 +1453,14 @@ mod tests { reason: "no change".into(), }); let built = order_revision_decision_event_build( - payload.root_event_id.as_str(), - payload.prev_event_id.as_str(), + &payload.root_event_id, + &payload.prev_event_id, &payload, ) .unwrap(); let mut event = RadrootsNostrEvent { - id: "event-id".into(), - author: "buyer".into(), + id: event_id_wire('e'), + author: buyer_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1411,7 +1470,7 @@ mod tests { let envelope = order_revision_decision_from_event(&event).unwrap(); assert_eq!(envelope.payload, payload); - event.author = "seller".into(); + event.author = seller_pubkey_wire(); let err = order_revision_decision_from_event(&event).unwrap_err(); assert_eq!(err, RadrootsOrderEnvelopeParseError::AuthorMismatch); } @@ -1432,16 +1491,16 @@ mod tests { let envelope = RadrootsOrderEnvelope::new(message_type, listing_addr_wire(), "order-1", &payload); let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind, tags: vec![ - vec!["p".into(), "buyer".into()], + vec!["p".into(), buyer_pubkey_wire()], vec!["a".into(), listing_addr_wire()], vec![TAG_D.into(), "order-1".into()], - vec![TAG_E_ROOT.into(), "root-event".into()], - vec![TAG_E_PREV.into(), "prev-event".into()], + vec![TAG_E_ROOT.into(), event_id_wire('1')], + vec![TAG_E_PREV.into(), event_id_wire('9')], ], content: serde_json::to_string(&envelope).unwrap(), sig: "sig".into(), @@ -1456,8 +1515,8 @@ mod tests { #[test] fn order_parse_rejects_forbidden_kind() { let event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: 3431, tags: Vec::new(), @@ -1471,10 +1530,12 @@ mod tests { #[test] fn order_parse_rejects_missing_required_refs() { let payload = order_decision(); - let built = order_decision_event_build("root-event", "prev-event", &payload).unwrap(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = order_decision_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); let mut event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags, @@ -1494,8 +1555,8 @@ mod tests { let payload = order_request(); let built = order_request_event_build(&listing_event_ptr(), &payload).unwrap(); let mut event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: built.kind, tags: built.tags.clone(), @@ -1505,8 +1566,8 @@ mod tests { let err = order_request_from_event(&event).unwrap_err(); assert_eq!(err, RadrootsOrderEnvelopeParseError::AuthorMismatch); - event.author = "buyer".into(); - event.tags[0] = vec!["p".into(), "other-seller".into()]; + event.author = buyer_pubkey_wire(); + event.tags[0] = vec!["p".into(), pubkey('c').into_string()]; let err = order_request_from_event(&event).unwrap_err(); assert_eq!( err, @@ -1517,11 +1578,13 @@ mod tests { #[test] fn order_buyer_lifecycle_parse_rejects_wrong_actor_or_counterparty() { let cancellation = order_cancelled(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); let cancellation_parts = - order_cancellation_event_build("root-event", "prev-event", &cancellation).unwrap(); + order_cancellation_event_build(&root_event_id, &prev_event_id, &cancellation).unwrap(); let cancellation_event = RadrootsNostrEvent { - id: "event-id".into(), - author: "seller".into(), + id: event_id_wire('e'), + author: seller_pubkey_wire(), created_at: 1, kind: cancellation_parts.kind, tags: cancellation_parts.tags, @@ -1533,21 +1596,52 @@ mod tests { let receipt = order_buyer_receipt(true); let receipt_parts = - order_receipt_event_build("root-event", "prev-event", &receipt).unwrap(); + order_receipt_event_build(&root_event_id, &prev_event_id, &receipt).unwrap(); let mut receipt_event = RadrootsNostrEvent { - id: "event-id".into(), - author: "buyer".into(), + id: event_id_wire('e'), + author: buyer_pubkey_wire(), created_at: 1, kind: receipt_parts.kind, tags: receipt_parts.tags, content: receipt_parts.content, sig: "sig".into(), }; - receipt_event.tags[0] = vec!["p".into(), "other-seller".into()]; + receipt_event.tags[0] = vec!["p".into(), pubkey('c').into_string()]; let err = order_receipt_from_event(&receipt_event).unwrap_err(); assert_eq!( err, RadrootsOrderEnvelopeParseError::CounterpartyTagMismatch ); } + + #[test] + fn order_parse_rejects_invalid_protocol_tag_values() { + let payload = order_decision(); + let root_event_id = event_id('1'); + let prev_event_id = event_id('9'); + let built = order_decision_event_build(&root_event_id, &prev_event_id, &payload).unwrap(); + let mut event = RadrootsNostrEvent { + id: event_id_wire('e'), + author: seller_pubkey_wire(), + created_at: 1, + kind: built.kind, + tags: built.tags, + content: built.content, + sig: "sig".into(), + }; + + event.tags[0] = vec!["p".into(), "not-a-pubkey".into()]; + let err = order_decision_from_event(&event).unwrap_err(); + assert_eq!(err, RadrootsOrderEnvelopeParseError::InvalidTag("p")); + + event.tags[0] = vec!["p".into(), buyer_pubkey_wire()]; + let root_tag = event + .tags + .iter_mut() + .find(|tag| tag.first().map(String::as_str) == Some(TAG_E_ROOT)) + .unwrap(); + root_tag[1] = "not-an-event-id".into(); + let err = order_decision_from_event(&event).unwrap_err(); + assert_eq!(err, RadrootsOrderEnvelopeParseError::InvalidTag(TAG_E_ROOT)); + } } diff --git a/crates/events_codec/src/order/encode.rs b/crates/events_codec/src/order/encode.rs @@ -4,6 +4,7 @@ use alloc::string::String; #[cfg(feature = "serde_json")] use radroots_events::{ RadrootsNostrEventPtr, + ids::RadrootsEventId, order::{ RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderEnvelope, RadrootsOrderEnvelopeError, RadrootsOrderEventType, RadrootsOrderFulfillmentUpdate, @@ -100,8 +101,8 @@ fn order_envelope_event_build<T: serde::Serialize>( listing_addr: &str, order_id: &str, listing_event: Option<&RadrootsNostrEventPtr>, - root_event_id: Option<&str>, - prev_event_id: Option<&str>, + root_event_id: Option<&RadrootsEventId>, + prev_event_id: Option<&RadrootsEventId>, payload: &T, ) -> Result<WireEventParts, EventEncodeError> { if message_type.requires_listing_snapshot() && listing_event.is_none() { @@ -124,8 +125,8 @@ fn order_envelope_event_build<T: serde::Serialize>( listing_addr, Some(order_id), listing_event, - root_event_id, - prev_event_id, + root_event_id.map(RadrootsEventId::as_str), + prev_event_id.map(RadrootsEventId::as_str), )?; Ok(WireEventParts { kind: message_type.kind(), @@ -154,8 +155,8 @@ pub fn order_request_event_build( #[cfg(feature = "serde_json")] pub fn order_decision_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderDecision, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; @@ -173,15 +174,15 @@ pub fn order_decision_event_build( #[cfg(feature = "serde_json")] pub fn order_revision_proposal_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderRevisionProposal, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; - if payload.root_event_id != root_event_id { + if payload.root_event_id.as_str() != root_event_id.as_str() { return Err(EventEncodeError::InvalidField("root_event_id")); } - if payload.prev_event_id != prev_event_id { + if payload.prev_event_id.as_str() != prev_event_id.as_str() { return Err(EventEncodeError::InvalidField("prev_event_id")); } order_envelope_event_build( @@ -198,15 +199,15 @@ pub fn order_revision_proposal_event_build( #[cfg(feature = "serde_json")] pub fn order_revision_decision_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderRevisionDecision, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; - if payload.root_event_id != root_event_id { + if payload.root_event_id.as_str() != root_event_id.as_str() { return Err(EventEncodeError::InvalidField("root_event_id")); } - if payload.prev_event_id != prev_event_id { + if payload.prev_event_id.as_str() != prev_event_id.as_str() { return Err(EventEncodeError::InvalidField("prev_event_id")); } order_envelope_event_build( @@ -223,8 +224,8 @@ pub fn order_revision_decision_event_build( #[cfg(feature = "serde_json")] pub fn order_fulfillment_update_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderFulfillmentUpdate, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; @@ -242,8 +243,8 @@ pub fn order_fulfillment_update_event_build( #[cfg(feature = "serde_json")] pub fn order_cancellation_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderCancellation, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; @@ -261,8 +262,8 @@ pub fn order_cancellation_event_build( #[cfg(feature = "serde_json")] pub fn order_receipt_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderReceipt, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; @@ -280,15 +281,15 @@ pub fn order_receipt_event_build( #[cfg(feature = "serde_json")] pub fn order_payment_record_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderPaymentRecord, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; - if payload.root_event_id != root_event_id { + if payload.root_event_id.as_str() != root_event_id.as_str() { return Err(EventEncodeError::InvalidField("root_event_id")); } - if payload.previous_event_id != prev_event_id { + if payload.previous_event_id.as_str() != prev_event_id.as_str() { return Err(EventEncodeError::InvalidField("previous_event_id")); } order_envelope_event_build( @@ -305,15 +306,15 @@ pub fn order_payment_record_event_build( #[cfg(feature = "serde_json")] pub fn order_settlement_decision_event_build( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderSettlementDecision, ) -> Result<WireEventParts, EventEncodeError> { payload.validate().map_err(map_order_payload_error)?; - if payload.root_event_id != root_event_id { + if payload.root_event_id.as_str() != root_event_id.as_str() { return Err(EventEncodeError::InvalidField("root_event_id")); } - if payload.previous_event_id != prev_event_id { + if payload.previous_event_id.as_str() != prev_event_id.as_str() { return Err(EventEncodeError::InvalidField("previous_event_id")); } order_envelope_event_build( diff --git a/crates/sdk/src/client.rs b/crates/sdk/src/client.rs @@ -33,6 +33,7 @@ use crate::{ ) ))] use core::time::Duration; +use radroots_events::ids::RadrootsEventId; #[cfg(feature = "radrootsd-client")] use radroots_events::kinds::{KIND_FARM, KIND_LISTING}; @@ -2012,8 +2013,8 @@ impl<'a> TradeClient<'a> { #[cfg(feature = "serde_json")] pub fn build_order_decision_draft( &self, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderDecision, ) -> Result<order::RadrootsOrderDecisionDraft, order::EventEncodeError> { order::build_order_decision_draft(root_event_id, prev_event_id, payload) @@ -2022,8 +2023,8 @@ impl<'a> TradeClient<'a> { #[cfg(feature = "serde_json")] pub fn build_order_revision_proposal_draft( &self, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderRevisionProposal, ) -> Result<order::RadrootsOrderRevisionProposalDraft, order::EventEncodeError> { order::build_order_revision_proposal_draft(root_event_id, prev_event_id, payload) @@ -2032,8 +2033,8 @@ impl<'a> TradeClient<'a> { #[cfg(feature = "serde_json")] pub fn build_order_revision_decision_draft( &self, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderRevisionDecision, ) -> Result<order::RadrootsOrderRevisionDecisionDraft, order::EventEncodeError> { order::build_order_revision_decision_draft(root_event_id, prev_event_id, payload) @@ -2042,8 +2043,8 @@ impl<'a> TradeClient<'a> { #[cfg(feature = "serde_json")] pub fn build_fulfillment_update_draft( &self, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderFulfillmentUpdate, ) -> Result<order::RadrootsOrderFulfillmentUpdateDraft, order::EventEncodeError> { order::build_fulfillment_update_draft(root_event_id, prev_event_id, payload) @@ -2052,8 +2053,8 @@ impl<'a> TradeClient<'a> { #[cfg(feature = "serde_json")] pub fn build_order_cancellation_draft( &self, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderCancellation, ) -> Result<order::RadrootsOrderCancellationDraft, order::EventEncodeError> { order::build_order_cancellation_draft(root_event_id, prev_event_id, payload) @@ -2062,8 +2063,8 @@ impl<'a> TradeClient<'a> { #[cfg(feature = "serde_json")] pub fn build_buyer_receipt_draft( &self, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderReceipt, ) -> Result<order::RadrootsOrderReceiptDraft, order::EventEncodeError> { order::build_buyer_receipt_draft(root_event_id, prev_event_id, payload) @@ -2176,8 +2177,8 @@ impl<'a> TradeClient<'a> { pub async fn publish_order_revision_proposal_with_identity( &self, identity: &RadrootsIdentity, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderRevisionProposal, ) -> Result<SdkPublishReceipt, SdkPublishError> { let draft = @@ -2200,8 +2201,8 @@ impl<'a> TradeClient<'a> { pub async fn publish_order_revision_decision_with_identity( &self, identity: &RadrootsIdentity, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderRevisionDecision, ) -> Result<SdkPublishReceipt, SdkPublishError> { let draft = @@ -2224,8 +2225,8 @@ impl<'a> TradeClient<'a> { pub async fn publish_order_decision_with_identity( &self, identity: &RadrootsIdentity, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderDecision, ) -> Result<SdkPublishReceipt, SdkPublishError> { let draft = order::build_order_decision_draft(root_event_id, prev_event_id, payload) @@ -2247,8 +2248,8 @@ impl<'a> TradeClient<'a> { pub async fn publish_fulfillment_update_with_identity( &self, identity: &RadrootsIdentity, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderFulfillmentUpdate, ) -> Result<SdkPublishReceipt, SdkPublishError> { let draft = order::build_fulfillment_update_draft(root_event_id, prev_event_id, payload) @@ -2308,8 +2309,8 @@ impl<'a> TradeClient<'a> { pub async fn publish_order_cancellation_with_identity( &self, identity: &RadrootsIdentity, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderCancellation, ) -> Result<SdkPublishReceipt, SdkPublishError> { let draft = order::build_order_cancellation_draft(root_event_id, prev_event_id, payload) @@ -2331,8 +2332,8 @@ impl<'a> TradeClient<'a> { pub async fn publish_buyer_receipt_with_identity( &self, identity: &RadrootsIdentity, - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &order::RadrootsOrderReceipt, ) -> Result<SdkPublishReceipt, SdkPublishError> { let draft = order::build_buyer_receipt_draft(root_event_id, prev_event_id, payload) diff --git a/crates/sdk/src/order.rs b/crates/sdk/src/order.rs @@ -9,6 +9,7 @@ pub use radroots_events_codec::order::{ pub use radroots_trade::listing::validation::RadrootsTradeListing as TradeListingValidateResult; use crate::{RadrootsNostrEvent, RadrootsNostrEventPtr, WireEventParts}; +use radroots_events::ids::RadrootsEventId; #[derive(Debug, Clone)] pub struct RadrootsOrderRequestDraft { @@ -127,8 +128,8 @@ pub fn build_order_request_draft( #[cfg(feature = "serde_json")] pub fn build_order_decision_draft( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderDecision, ) -> Result<RadrootsOrderDecisionDraft, EventEncodeError> { Ok(RadrootsOrderDecisionDraft { @@ -142,8 +143,8 @@ pub fn build_order_decision_draft( #[cfg(feature = "serde_json")] pub fn build_order_revision_proposal_draft( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderRevisionProposal, ) -> Result<RadrootsOrderRevisionProposalDraft, EventEncodeError> { Ok(RadrootsOrderRevisionProposalDraft { @@ -157,8 +158,8 @@ pub fn build_order_revision_proposal_draft( #[cfg(feature = "serde_json")] pub fn build_order_revision_decision_draft( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderRevisionDecision, ) -> Result<RadrootsOrderRevisionDecisionDraft, EventEncodeError> { Ok(RadrootsOrderRevisionDecisionDraft { @@ -172,8 +173,8 @@ pub fn build_order_revision_decision_draft( #[cfg(feature = "serde_json")] pub fn build_fulfillment_update_draft( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderFulfillmentUpdate, ) -> Result<RadrootsOrderFulfillmentUpdateDraft, EventEncodeError> { Ok(RadrootsOrderFulfillmentUpdateDraft { @@ -187,8 +188,8 @@ pub fn build_fulfillment_update_draft( #[cfg(feature = "serde_json")] pub fn build_order_cancellation_draft( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderCancellation, ) -> Result<RadrootsOrderCancellationDraft, EventEncodeError> { Ok(RadrootsOrderCancellationDraft { @@ -202,8 +203,8 @@ pub fn build_order_cancellation_draft( #[cfg(feature = "serde_json")] pub fn build_buyer_receipt_draft( - root_event_id: &str, - prev_event_id: &str, + root_event_id: &RadrootsEventId, + prev_event_id: &RadrootsEventId, payload: &RadrootsOrderReceipt, ) -> Result<RadrootsOrderReceiptDraft, EventEncodeError> { Ok(RadrootsOrderReceiptDraft { diff --git a/crates/sdk/tests/client.rs b/crates/sdk/tests/client.rs @@ -3,6 +3,7 @@ use radroots_core::{ 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_FULFILLMENT_UPDATE, KIND_ORDER_RECEIPT, KIND_ORDER_REQUEST, @@ -129,12 +130,29 @@ fn usd(raw: &str) -> RadrootsCoreMoney { fn listing_event_ptr() -> RadrootsNostrEventPtr { RadrootsNostrEventPtr { - id: "listing-event-1".into(), + id: event_id_wire('a'), relays: Some("wss://listing.relay.example".into()), } } +fn public_key(value: String) -> RadrootsPublicKey { + value.parse().expect("public key") +} + +fn event_id(character: char) -> RadrootsEventId { + core::iter::repeat_n(character, 64) + .collect::<String>() + .parse() + .expect("event id") +} + +fn event_id_wire(character: char) -> String { + event_id(character).into_string() +} + fn sample_order_request(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderRequest { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderRequest { order_id: "order-1".parse().expect("order id"), listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -171,6 +189,8 @@ fn sample_order_request(buyer_pubkey: String, seller_pubkey: String) -> Radroots } fn sample_order_decision(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderDecision { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderDecision { order_id: "order-1".parse().expect("order id"), listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -193,6 +213,8 @@ fn sample_order_revision_proposal( root_event_id: String, prev_event_id: String, ) -> RadrootsOrderRevisionProposal { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderRevisionProposal { revision_id: "revision-1".parse().expect("revision id"), order_id: "order-1".parse().expect("order id"), @@ -201,8 +223,8 @@ fn sample_order_revision_proposal( .expect("listing address"), buyer_pubkey, seller_pubkey, - root_event_id, - prev_event_id, + root_event_id: root_event_id.parse().expect("root event id"), + prev_event_id: prev_event_id.parse().expect("previous event id"), items: vec![RadrootsOrderItem { bin_id: "bin-1".parse().expect("bin id"), bin_count: 3, @@ -243,7 +265,7 @@ fn sample_order_revision_decision( buyer_pubkey: proposal.buyer_pubkey.clone(), seller_pubkey: proposal.seller_pubkey.clone(), root_event_id: proposal.root_event_id.clone(), - prev_event_id: "order-revision-proposal-event-1".into(), + prev_event_id: event_id('3'), decision, } } @@ -252,6 +274,8 @@ fn sample_fulfillment_update( buyer_pubkey: String, seller_pubkey: String, ) -> RadrootsOrderFulfillmentUpdate { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderFulfillmentUpdate { order_id: "order-1".parse().expect("order id"), listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -267,6 +291,8 @@ fn sample_order_cancellation( buyer_pubkey: String, seller_pubkey: String, ) -> RadrootsOrderCancellation { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderCancellation { order_id: "order-1".parse().expect("order id"), listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -279,6 +305,8 @@ fn sample_order_cancellation( } fn sample_buyer_receipt(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderReceipt { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderReceipt { order_id: "order-1".parse().expect("order id"), listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -527,10 +555,10 @@ fn order_facades_round_trip_all_draft_types() { let order_client = client.order(); let buyer_pubkey = "b".repeat(64); let seller_pubkey = "a".repeat(64); - let root_event_id = "order-request-event-1"; - let decision_event_id = "order-decision-event-1"; - let proposal_event_id = "order-revision-proposal-event-1"; - let fulfillment_event_id = "fulfillment-event-1"; + let root_event_id = event_id('1'); + let decision_event_id = event_id('2'); + let proposal_event_id = event_id('3'); + let fulfillment_event_id = event_id('4'); let order_request = sample_order_request(buyer_pubkey.clone(), seller_pubkey.clone()); let order_draft = order_client @@ -538,7 +566,7 @@ fn order_facades_round_trip_all_draft_types() { .expect("order request draft"); assert_eq!(order_draft.as_wire_parts().kind, KIND_ORDER_REQUEST); let order_event = event_from_parts( - root_event_id, + root_event_id.as_str(), &buyer_pubkey, 1, order_draft.clone().into_wire_parts(), @@ -550,11 +578,11 @@ fn order_facades_round_trip_all_draft_types() { let decision = sample_order_decision(buyer_pubkey.clone(), seller_pubkey.clone()); let decision_draft = order_client - .build_order_decision_draft(root_event_id, root_event_id, &decision) + .build_order_decision_draft(&root_event_id, &root_event_id, &decision) .expect("order decision draft"); assert_eq!(decision_draft.as_wire_parts().kind, KIND_ORDER_DECISION); let decision_event = event_from_parts( - decision_event_id, + decision_event_id.as_str(), &seller_pubkey, 2, decision_draft.clone().into_wire_parts(), @@ -571,18 +599,18 @@ fn order_facades_round_trip_all_draft_types() { let proposal = sample_order_revision_proposal( buyer_pubkey.clone(), seller_pubkey.clone(), - root_event_id.into(), - decision_event_id.into(), + root_event_id.to_string(), + decision_event_id.to_string(), ); let proposal_draft = order_client - .build_order_revision_proposal_draft(root_event_id, decision_event_id, &proposal) + .build_order_revision_proposal_draft(&root_event_id, &decision_event_id, &proposal) .expect("revision proposal draft"); assert_eq!( proposal_draft.as_wire_parts().kind, KIND_ORDER_REVISION_PROPOSAL ); let proposal_event = event_from_parts( - proposal_event_id, + proposal_event_id.as_str(), &seller_pubkey, 3, proposal_draft.clone().into_wire_parts(), @@ -601,8 +629,8 @@ fn order_facades_round_trip_all_draft_types() { sample_order_revision_decision(&proposal, RadrootsOrderRevisionOutcome::Accepted); let revision_decision_draft = order_client .build_order_revision_decision_draft( - root_event_id, - revision_decision.prev_event_id.as_str(), + &root_event_id, + &revision_decision.prev_event_id, &revision_decision, ) .expect("revision decision draft"); @@ -627,14 +655,14 @@ fn order_facades_round_trip_all_draft_types() { let fulfillment = sample_fulfillment_update(buyer_pubkey.clone(), seller_pubkey.clone()); let fulfillment_draft = order_client - .build_fulfillment_update_draft(root_event_id, decision_event_id, &fulfillment) + .build_fulfillment_update_draft(&root_event_id, &decision_event_id, &fulfillment) .expect("fulfillment draft"); assert_eq!( fulfillment_draft.as_wire_parts().kind, KIND_ORDER_FULFILLMENT_UPDATE ); let fulfillment_event = event_from_parts( - fulfillment_event_id, + fulfillment_event_id.as_str(), &seller_pubkey, 5, fulfillment_draft.clone().into_wire_parts(), @@ -650,7 +678,7 @@ fn order_facades_round_trip_all_draft_types() { let cancellation = sample_order_cancellation(buyer_pubkey.clone(), seller_pubkey.clone()); let cancellation_draft = order_client - .build_order_cancellation_draft(root_event_id, decision_event_id, &cancellation) + .build_order_cancellation_draft(&root_event_id, &decision_event_id, &cancellation) .expect("cancellation draft"); assert_eq!( cancellation_draft.as_wire_parts().kind, @@ -673,7 +701,7 @@ fn order_facades_round_trip_all_draft_types() { let receipt = sample_buyer_receipt(buyer_pubkey.clone(), seller_pubkey.clone()); let receipt_draft = order_client - .build_buyer_receipt_draft(root_event_id, fulfillment_event_id, &receipt) + .build_buyer_receipt_draft(&root_event_id, &fulfillment_event_id, &receipt) .expect("receipt draft"); assert_eq!(receipt_draft.as_wire_parts().kind, KIND_ORDER_RECEIPT); let receipt_event = event_from_parts( @@ -698,11 +726,11 @@ fn order_draft_facades_return_encoder_errors() { let order = client.order(); let buyer_pubkey = "b".repeat(64); let seller_pubkey = "a".repeat(64); - let root_event_id = "order-request-event-1"; - let decision_event_id = "order-decision-event-1"; + let root_event_id = event_id('1'); + let decision_event_id = event_id('2'); let mut invalid_order = sample_order_request(buyer_pubkey.clone(), seller_pubkey.clone()); - invalid_order.buyer_pubkey.clear(); + invalid_order.items.clear(); assert!( order .build_order_request_draft(&listing_event_ptr(), &invalid_order) @@ -710,32 +738,40 @@ fn order_draft_facades_return_encoder_errors() { ); let mut invalid_decision = sample_order_decision(buyer_pubkey.clone(), seller_pubkey.clone()); - invalid_decision.buyer_pubkey.clear(); + invalid_decision.decision = RadrootsOrderDecisionOutcome::Accepted { + inventory_commitments: Vec::new(), + }; assert!( order - .build_order_decision_draft(root_event_id, root_event_id, &invalid_decision) + .build_order_decision_draft(&root_event_id, &root_event_id, &invalid_decision) .is_err() ); let proposal = sample_order_revision_proposal( buyer_pubkey.clone(), seller_pubkey.clone(), - root_event_id.into(), - decision_event_id.into(), + root_event_id.to_string(), + decision_event_id.to_string(), ); + let different_root_event_id = event_id('d'); assert!( order - .build_order_revision_proposal_draft("different-root", decision_event_id, &proposal) + .build_order_revision_proposal_draft( + &different_root_event_id, + &decision_event_id, + &proposal, + ) .is_err() ); let revision_decision = sample_order_revision_decision(&proposal, RadrootsOrderRevisionOutcome::Accepted); + let different_prev_event_id = event_id('e'); assert!( order .build_order_revision_decision_draft( - root_event_id, - "different-prev", + &root_event_id, + &different_prev_event_id, &revision_decision, ) .is_err() @@ -745,7 +781,7 @@ fn order_draft_facades_return_encoder_errors() { fulfillment.status = RadrootsOrderFulfillmentState::AcceptedNotFulfilled; assert!( order - .build_fulfillment_update_draft(root_event_id, decision_event_id, &fulfillment) + .build_fulfillment_update_draft(&root_event_id, &decision_event_id, &fulfillment) .is_err() ); @@ -753,7 +789,7 @@ fn order_draft_facades_return_encoder_errors() { cancellation.reason.clear(); assert!( order - .build_order_cancellation_draft(root_event_id, decision_event_id, &cancellation) + .build_order_cancellation_draft(&root_event_id, &decision_event_id, &cancellation) .is_err() ); @@ -761,7 +797,7 @@ fn order_draft_facades_return_encoder_errors() { receipt.received = false; assert!( order - .build_buyer_receipt_draft(root_event_id, decision_event_id, &receipt) + .build_buyer_receipt_draft(&root_event_id, &decision_event_id, &receipt) .is_err() ); } diff --git a/crates/sdk/tests/facade.rs b/crates/sdk/tests/facade.rs @@ -3,6 +3,7 @@ use radroots_core::{ RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; use radroots_events::farm::{RadrootsFarm, RadrootsFarmRef}; +use radroots_events::ids::RadrootsPublicKey; use radroots_events::kinds::{KIND_FARM, KIND_LISTING, KIND_ORDER_REQUEST, KIND_PROFILE}; use radroots_events::listing::{ RadrootsListing, RadrootsListingAvailability, RadrootsListingBin, @@ -122,21 +123,28 @@ fn listing_event(listing_value: &RadrootsListing) -> RadrootsNostrEvent { fn listing_event_ptr() -> RadrootsNostrEventPtr { RadrootsNostrEventPtr { - id: "listing-event-1".into(), + 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 sample_order_request() -> RadrootsOrderRequest { - let seller_pubkey = "a".repeat(64); + 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: "buyer".into(), - seller_pubkey: "seller".into(), + buyer_pubkey: public_key('b'), + seller_pubkey, items: vec![RadrootsOrderItem { bin_id: "bin-1".parse().expect("bin id"), bin_count: 2, @@ -247,8 +255,8 @@ fn order_facade_wraps_build_parse_and_address_ops() { assert_eq!(parsed_addr.listing_id, listing_value.d_tag); let event = RadrootsNostrEvent { - id: "order-event".into(), - author: "buyer".into(), + id: core::iter::repeat_n('b', 64).collect(), + author: payload.buyer_pubkey.to_string(), created_at: 2, kind: parts.as_wire_parts().kind, tags: parts.as_wire_parts().tags.clone(), diff --git a/crates/sdk/tests/radrootsd.rs b/crates/sdk/tests/radrootsd.rs @@ -5,6 +5,7 @@ use radroots_core::{ RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; use radroots_events::farm::{RadrootsFarm, RadrootsFarmLocation, RadrootsFarmRef}; +use radroots_events::ids::RadrootsPublicKey; use radroots_events::kinds::{ KIND_FARM, KIND_LISTING, KIND_LISTING_DRAFT, KIND_ORDER_REQUEST, KIND_PROFILE, }; @@ -451,15 +452,15 @@ fn sample_order_request_economics() -> RadrootsOrderEconomics { } fn sample_order_request() -> RadrootsOrderRequest { - let seller_pubkey = "a".repeat(64); + let seller_pubkey: RadrootsPublicKey = "a".repeat(64).parse().expect("seller public key"); RadrootsOrderRequest { order_id: "order-1".parse().expect("order id"), listing_addr: format!("{KIND_LISTING}:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") .parse() .expect("listing address"), - buyer_pubkey: "buyer".to_owned(), - seller_pubkey: "seller".to_owned(), + buyer_pubkey: "b".repeat(64).parse().expect("buyer public key"), + seller_pubkey, items: vec![RadrootsOrderItem { bin_id: "bin-1".parse().expect("bin id"), bin_count: 2, @@ -470,7 +471,7 @@ fn sample_order_request() -> RadrootsOrderRequest { fn listing_event_ptr_with_relays(relays: Option<&str>) -> RadrootsNostrEventPtr { RadrootsNostrEventPtr { - id: "listing-event-1".to_owned(), + id: "a".repeat(64), relays: relays.map(str::to_owned), } } @@ -1628,7 +1629,7 @@ async fn radrootsd_order_request_publish_accepts_session_handle() -> TestResult< assert_eq!(request_json["params"]["order"]["order_id"], "order-1"); assert_eq!( request_json["params"]["listing_event"]["id"], - "listing-event-1" + "a".repeat(64) ); assert_eq!( request_json["params"]["listing_event"]["relays"], @@ -1760,7 +1761,7 @@ async fn radrootsd_sdk_workflow_chains_session_listing_order_and_bridge_job() -> assert_eq!(order_request["params"]["order"]["order_id"], "order-1"); assert_eq!( order_request["params"]["listing_event"]["id"], - "listing-event-1" + "a".repeat(64) ); let order_job = match &order_receipt.transport_receipt { diff --git a/crates/sdk/tests/relay_direct.rs b/crates/sdk/tests/relay_direct.rs @@ -10,6 +10,7 @@ use radroots_core::{ RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity, RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; +use radroots_events::ids::{RadrootsEventId, RadrootsPublicKey}; use radroots_sdk::farm::{RadrootsFarm, RadrootsFarmLocation, RadrootsFarmRef}; use radroots_sdk::identity::RadrootsIdentity; use radroots_sdk::listing::{ @@ -214,12 +215,29 @@ fn usd(raw: &str) -> RadrootsCoreMoney { fn listing_event_ptr() -> RadrootsNostrEventPtr { RadrootsNostrEventPtr { - id: "listing-event-1".into(), + id: event_id_wire('a'), relays: Some("wss://listing.relay.example".into()), } } +fn public_key(value: String) -> RadrootsPublicKey { + value.parse().expect("public key") +} + +fn event_id(character: char) -> RadrootsEventId { + core::iter::repeat_n(character, 64) + .collect::<String>() + .parse() + .expect("event id") +} + +fn event_id_wire(character: char) -> String { + event_id(character).into_string() +} + fn sample_order_request(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderRequest { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderRequest { order_id: "order-1".parse().expect("order id"), listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -256,6 +274,8 @@ fn sample_order_request(buyer_pubkey: String, seller_pubkey: String) -> Radroots } fn sample_order_decision(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderDecision { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderDecision { order_id: "order-1".parse().expect("order id"), listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -278,6 +298,8 @@ fn sample_order_revision_proposal( root_event_id: String, prev_event_id: String, ) -> RadrootsOrderRevisionProposal { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderRevisionProposal { revision_id: "revision-1".parse().expect("revision id"), order_id: "order-1".parse().expect("order id"), @@ -286,8 +308,8 @@ fn sample_order_revision_proposal( .expect("listing address"), buyer_pubkey, seller_pubkey, - root_event_id, - prev_event_id, + root_event_id: root_event_id.parse().expect("root event id"), + prev_event_id: prev_event_id.parse().expect("previous event id"), items: vec![RadrootsOrderItem { bin_id: "bin-1".parse().expect("bin id"), bin_count: 3, @@ -328,7 +350,7 @@ fn sample_order_revision_decision( buyer_pubkey: proposal.buyer_pubkey.clone(), seller_pubkey: proposal.seller_pubkey.clone(), root_event_id: proposal.root_event_id.clone(), - prev_event_id: "order-revision-proposal-event-1".into(), + prev_event_id: event_id('3'), decision, } } @@ -337,6 +359,8 @@ fn sample_fulfillment_update( buyer_pubkey: String, seller_pubkey: String, ) -> RadrootsOrderFulfillmentUpdate { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderFulfillmentUpdate { order_id: "order-1".parse().expect("order id"), listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -352,6 +376,8 @@ fn sample_order_cancellation( buyer_pubkey: String, seller_pubkey: String, ) -> RadrootsOrderCancellation { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderCancellation { order_id: "order-1".parse().expect("order id"), listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -364,6 +390,8 @@ fn sample_order_cancellation( } fn sample_buyer_receipt(buyer_pubkey: String, seller_pubkey: String) -> RadrootsOrderReceipt { + let buyer_pubkey = public_key(buyer_pubkey); + let seller_pubkey = public_key(seller_pubkey); RadrootsOrderReceipt { order_id: "order-1".parse().expect("order id"), listing_addr: format!("30402:{seller_pubkey}:AAAAAAAAAAAAAAAAAAAAAg") @@ -519,7 +547,7 @@ async fn relay_direct_order_decision_publish_accepts_sdk_built_draft() -> TestRe let relay = AckRelay::spawn().await?; let buyer_identity = RadrootsIdentity::generate(); let seller_identity = RadrootsIdentity::generate(); - let root_event_id = "order-request-event-1"; + let root_event_id = event_id('1'); let payload = sample_order_decision( buyer_identity.public_key_hex(), seller_identity.public_key_hex(), @@ -534,7 +562,7 @@ async fn relay_direct_order_decision_publish_accepts_sdk_built_draft() -> TestRe let draft = client .order() - .build_order_decision_draft(root_event_id, root_event_id, &payload)?; + .build_order_decision_draft(&root_event_id, &root_event_id, &payload)?; assert_eq!(draft.as_wire_parts().kind, 3423); let receipt = client @@ -576,13 +604,13 @@ async fn relay_direct_order_decision_publish_accepts_sdk_built_draft() -> TestRe relay_receipt .event .tags - .contains(&vec!["e_root".to_owned(), root_event_id.to_owned(),]) + .contains(&vec!["e_root".to_owned(), root_event_id.to_string()]) ); assert!( relay_receipt .event .tags - .contains(&vec!["e_prev".to_owned(), root_event_id.to_owned(),]) + .contains(&vec!["e_prev".to_owned(), root_event_id.to_string()]) ); assert_eq!(relay_receipt.target_relays, vec![relay.url().to_owned()]); assert_eq!(relay_receipt.connected_relays, vec![relay.url().to_owned()]); @@ -612,13 +640,13 @@ async fn relay_direct_order_revision_publish_accepts_sdk_built_payloads() -> Tes let seller_identity = RadrootsIdentity::generate(); let buyer_pubkey = buyer_identity.public_key_hex(); let seller_pubkey = seller_identity.public_key_hex(); - let root_event_id = "order-request-event-1"; - let decision_event_id = "order-decision-event-1"; + let root_event_id = event_id('1'); + let decision_event_id = event_id('2'); let proposal = sample_order_revision_proposal( buyer_pubkey.clone(), seller_pubkey.clone(), - root_event_id.to_owned(), - decision_event_id.to_owned(), + root_event_id.to_string(), + decision_event_id.to_string(), ); let decision = sample_order_revision_decision(&proposal, RadrootsOrderRevisionOutcome::Accepted); @@ -634,8 +662,8 @@ async fn relay_direct_order_revision_publish_accepts_sdk_built_payloads() -> Tes .order() .publish_order_revision_proposal_with_identity( &seller_identity, - root_event_id, - decision_event_id, + &root_event_id, + &decision_event_id, &proposal, ) .await?; @@ -643,8 +671,8 @@ async fn relay_direct_order_revision_publish_accepts_sdk_built_payloads() -> Tes .order() .publish_order_revision_decision_with_identity( &buyer_identity, - root_event_id, - decision.prev_event_id.as_str(), + &root_event_id, + &decision.prev_event_id, &decision, ) .await?; @@ -666,13 +694,13 @@ async fn relay_direct_order_revision_publish_accepts_sdk_built_payloads() -> Tes relay_receipt .event .tags - .contains(&vec!["e_root".to_owned(), root_event_id.to_owned()]) + .contains(&vec!["e_root".to_owned(), root_event_id.to_string()]) ); assert!( relay_receipt .event .tags - .contains(&vec!["e_prev".to_owned(), decision_event_id.to_owned()]) + .contains(&vec!["e_prev".to_owned(), decision_event_id.to_string()]) ); let envelope = client .order() @@ -701,12 +729,14 @@ async fn relay_direct_order_revision_publish_accepts_sdk_built_payloads() -> Tes relay_receipt .event .tags - .contains(&vec!["e_root".to_owned(), root_event_id.to_owned()]) + .contains(&vec!["e_root".to_owned(), root_event_id.to_string()]) + ); + assert!( + relay_receipt + .event + .tags + .contains(&vec!["e_prev".to_owned(), event_id_wire('3')]) ); - assert!(relay_receipt.event.tags.contains(&vec![ - "e_prev".to_owned(), - "order-revision-proposal-event-1".to_owned() - ])); let envelope = client .order() .parse_order_revision_decision(&relay_receipt.event) @@ -732,9 +762,9 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te let seller_identity = RadrootsIdentity::generate(); let buyer_pubkey = buyer_identity.public_key_hex(); let seller_pubkey = seller_identity.public_key_hex(); - let root_event_id = "order-request-event-1"; - let decision_event_id = "order-decision-event-1"; - let fulfillment_event_id = "fulfillment-event-1"; + let root_event_id = event_id('1'); + let decision_event_id = event_id('2'); + let fulfillment_event_id = event_id('4'); let fulfillment = sample_fulfillment_update(buyer_pubkey.clone(), seller_pubkey.clone()); let cancellation = sample_order_cancellation(buyer_pubkey.clone(), seller_pubkey.clone()); let receipt = sample_buyer_receipt(buyer_pubkey.clone(), seller_pubkey.clone()); @@ -750,8 +780,8 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te .order() .publish_fulfillment_update_with_identity( &seller_identity, - root_event_id, - decision_event_id, + &root_event_id, + &decision_event_id, &fulfillment, ) .await?; @@ -759,8 +789,8 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te .order() .publish_order_cancellation_with_identity( &buyer_identity, - root_event_id, - root_event_id, + &root_event_id, + &root_event_id, &cancellation, ) .await?; @@ -768,8 +798,8 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te .order() .publish_buyer_receipt_with_identity( &buyer_identity, - root_event_id, - fulfillment_event_id, + &root_event_id, + &fulfillment_event_id, &receipt, ) .await?; @@ -792,13 +822,13 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te relay_receipt .event .tags - .contains(&vec!["e_root".to_owned(), root_event_id.to_owned()]) + .contains(&vec!["e_root".to_owned(), root_event_id.to_string()]) ); assert!( relay_receipt .event .tags - .contains(&vec!["e_prev".to_owned(), decision_event_id.to_owned()]) + .contains(&vec!["e_prev".to_owned(), decision_event_id.to_string()]) ); let envelope = client .order() @@ -825,13 +855,13 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te relay_receipt .event .tags - .contains(&vec!["e_root".to_owned(), root_event_id.to_owned()]) + .contains(&vec!["e_root".to_owned(), root_event_id.to_string()]) ); assert!( relay_receipt .event .tags - .contains(&vec!["e_prev".to_owned(), root_event_id.to_owned()]) + .contains(&vec!["e_prev".to_owned(), root_event_id.to_string()]) ); let envelope = client .order() @@ -858,13 +888,13 @@ async fn relay_direct_order_lifecycle_publish_accepts_sdk_built_payloads() -> Te relay_receipt .event .tags - .contains(&vec!["e_root".to_owned(), root_event_id.to_owned()]) + .contains(&vec!["e_root".to_owned(), root_event_id.to_string()]) ); assert!( relay_receipt .event .tags - .contains(&vec!["e_prev".to_owned(), fulfillment_event_id.to_owned()]) + .contains(&vec!["e_prev".to_owned(), fulfillment_event_id.to_string()]) ); let envelope = client .order() @@ -896,13 +926,14 @@ async fn relay_direct_order_decision_publish_builds_and_publishes_payload() -> T urls: vec![relay.url().to_owned()], }; let client = RadrootsSdkClient::from_config(config)?; + let root_event_id = event_id('1'); let receipt = client .order() .publish_order_decision_with_identity( &seller_identity, - "order-request-event-1", - "order-request-event-1", + &root_event_id, + &root_event_id, &payload, ) .await?; diff --git a/crates/trade/src/order.rs b/crates/trade/src/order.rs @@ -903,21 +903,13 @@ pub fn canonicalize_order_request_for_signer( let listing_addr_raw = request.listing_addr.to_string(); let listing_addr = parse_public_listing_addr(&listing_addr_raw)?; - let buyer_pubkey = if request.buyer_pubkey.trim().is_empty() { - normalized_required_string(signer_pubkey.to_string(), "buyer_pubkey")? - } else { - normalized_required_string(core::mem::take(&mut request.buyer_pubkey), "buyer_pubkey")? - }; - if buyer_pubkey != signer_pubkey { + let buyer_pubkey = request.buyer_pubkey.clone(); + if buyer_pubkey.as_str() != signer_pubkey { return Err(RadrootsOrderCanonicalizationError::InvalidBuyerSigner); } - let seller_pubkey = if request.seller_pubkey.trim().is_empty() { - listing_addr.seller_pubkey.clone() - } else { - normalized_required_string(core::mem::take(&mut request.seller_pubkey), "seller_pubkey")? - }; - if seller_pubkey != listing_addr.seller_pubkey { + let seller_pubkey = request.seller_pubkey.clone(); + if seller_pubkey.as_str() != listing_addr.seller_pubkey { return Err(RadrootsOrderCanonicalizationError::InvalidSellerListing); } @@ -941,22 +933,14 @@ pub fn canonicalize_order_decision_for_signer( let listing_addr_raw = decision_event.listing_addr.to_string(); let listing_addr = parse_public_listing_addr(&listing_addr_raw)?; - let seller_pubkey = if decision_event.seller_pubkey.trim().is_empty() { - normalized_required_string(signer_pubkey.to_string(), "seller_pubkey")? - } else { - normalized_required_string( - core::mem::take(&mut decision_event.seller_pubkey), - "seller_pubkey", - )? - }; - if seller_pubkey != signer_pubkey || seller_pubkey != listing_addr.seller_pubkey { + let seller_pubkey = decision_event.seller_pubkey.clone(); + if seller_pubkey.as_str() != signer_pubkey + || seller_pubkey.as_str() != listing_addr.seller_pubkey + { return Err(RadrootsOrderCanonicalizationError::InvalidSellerListing); } - let buyer_pubkey = normalized_required_string( - core::mem::take(&mut decision_event.buyer_pubkey), - "buyer_pubkey", - )?; + let buyer_pubkey = decision_event.buyer_pubkey.clone(); canonicalize_decision(&mut decision_event.decision)?; decision_event.order_id = order_id; @@ -2223,7 +2207,7 @@ fn payment_projection_from_record( settlement_state, payment_event_id: Some(payment.event_id.clone()), settlement_event_id: settlement.map(|settlement| settlement.event_id.clone()), - agreement_event_id: Some(payment.payload.agreement_event_id.clone()), + agreement_event_id: Some(payment.payload.agreement_event_id.to_string()), quote_id: Some(payment.payload.quote_id.to_string()), quote_version: Some(payment.payload.quote_version), economics_digest: Some(payment.payload.economics_digest.to_string()), @@ -2836,8 +2820,8 @@ fn requested_projection( economics: Some(request.payload.economics.clone()), agreement_event_id: None, listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), - seller_pubkey: Some(request.payload.seller_pubkey.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), + seller_pubkey: Some(request.payload.seller_pubkey.to_string()), last_event_id: Some(request.event_id.clone()), issues: Vec::new(), } @@ -3145,8 +3129,8 @@ fn decided_projection( economics, agreement_event_id, listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), - seller_pubkey: Some(request.payload.seller_pubkey.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), + seller_pubkey: Some(request.payload.seller_pubkey.to_string()), last_event_id, issues: Vec::new(), } @@ -3264,8 +3248,8 @@ fn cancelled_projection( economics: Some(economics), agreement_event_id, listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), - seller_pubkey: Some(request.payload.seller_pubkey.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), + seller_pubkey: Some(request.payload.seller_pubkey.to_string()), last_event_id: Some(cancellation.event_id), issues: Vec::new(), } @@ -3302,8 +3286,8 @@ fn receipt_terminal_projection( economics: Some(economics.clone()), agreement_event_id: Some(agreement_event_id.to_string()), listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), - seller_pubkey: Some(request.payload.seller_pubkey.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), + seller_pubkey: Some(request.payload.seller_pubkey.to_string()), last_event_id: Some(receipt.event_id), issues: Vec::new(), } @@ -3351,8 +3335,8 @@ fn invalid_projection_with_payment( economics, agreement_event_id: None, listing_addr: request.map(|request| request.payload.listing_addr.to_string()), - buyer_pubkey: request.map(|request| request.payload.buyer_pubkey.clone()), - seller_pubkey: request.map(|request| request.payload.seller_pubkey.clone()), + buyer_pubkey: request.map(|request| request.payload.buyer_pubkey.to_string()), + seller_pubkey: request.map(|request| request.payload.seller_pubkey.to_string()), last_event_id: request.map(|request| request.event_id.clone()), issues, }