lexe_api_core/models/command.rs
1use std::collections::BTreeSet;
2
3use bitcoin::address::NetworkUnchecked;
4#[cfg(doc)]
5use lexe_common::root_seed::RootSeed;
6#[cfg(any(test, feature = "test-utils"))]
7use lexe_common::test_utils::arbitrary;
8use lexe_common::{
9 api::user::{NodePk, UserPk},
10 ln::{
11 amount::Amount,
12 balance::{LightningBalance, OnchainBalance},
13 channel::{LxChannelDetails, LxChannelId, LxUserChannelId},
14 hashes::Txid,
15 priority::ConfirmationPriority,
16 route::LxRoute,
17 },
18 time::TimestampMs,
19};
20use lexe_enclave::enclave::Measurement;
21use lexe_serde::hexstr_or_bytes;
22#[cfg(any(test, feature = "test-utils"))]
23use proptest_derive::Arbitrary;
24use serde::{Deserialize, Serialize};
25
26use crate::types::{
27 bounded_note::BoundedNote,
28 invoice::Invoice,
29 offer::{MaxQuantity, Offer},
30 payments::{
31 ClientPaymentId, PaymentCreatedIndex, PaymentId, PaymentUpdatedIndex,
32 },
33 username::Username,
34};
35
36// --- General --- //
37
38#[derive(Debug, Serialize, Deserialize)]
39pub struct NodeInfo {
40 pub version: semver::Version,
41 pub measurement: Measurement,
42 pub user_pk: UserPk,
43 pub node_pk: NodePk,
44 pub num_peers: usize,
45
46 pub num_usable_channels: usize,
47 pub num_channels: usize,
48 /// Our lightning channel balance
49 pub lightning_balance: LightningBalance,
50
51 /// Our on-chain wallet balance
52 pub onchain_balance: OnchainBalance,
53 /// The total # of UTXOs tracked by BDK.
54 pub num_utxos: usize,
55 /// The # of confirmed UTXOs tracked by BDK.
56 // TODO(max): LSP metrics should warn if this drops too low, as opening
57 // zeroconf with unconfirmed inputs risks double spending of channel funds.
58 pub num_confirmed_utxos: usize,
59 /// The # of unconfirmed UTXOs tracked by BDK.
60 pub num_unconfirmed_utxos: usize,
61
62 /// The channel manager's best synced block height.
63 pub best_block_height: u32,
64
65 /// The number of pending channel monitor updates.
66 /// If this isn't 0, it's likely that at least one channel is paused.
67 // TODO(max): This field is in the wrong place and should be removed.
68 // To my knowledge it is only used by integration tests (in a hacky way) to
69 // wait for a node to reach a quiescent state. The polling should be done
70 // inside the server handler rather than by the client in the test harness.
71 pub pending_monitor_updates: usize,
72}
73
74#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
75pub enum GDriveStatus {
76 Ok,
77 Error(String),
78 Disabled,
79}
80
81#[derive(Debug, Serialize, Deserialize)]
82pub struct BackupInfo {
83 pub gdrive_status: GDriveStatus,
84}
85
86/// Request to query which node enclaves need provisioning, given the client's
87/// trusted measurements.
88#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
89#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
90pub struct EnclavesToProvisionRequest {
91 /// The enclave measurements the client trusts.
92 /// Typically the 3 latest from releases.json.
93 pub trusted_measurements: BTreeSet<Measurement>,
94}
95
96#[derive(Debug, PartialEq, Serialize, Deserialize)]
97#[cfg_attr(test, derive(Arbitrary))]
98pub struct SetupGDrive {
99 /// The auth `code` which can used to obtain a set of GDrive credentials.
100 /// - Applicable only in staging/prod.
101 /// - If GDrive has not been setup, the node will acquire the full set of
102 /// GDrive credentials and persist them (encrypted ofc) in Lexe's DB.
103 #[cfg_attr(test, proptest(strategy = "arbitrary::any_string()"))]
104 pub google_auth_code: String,
105
106 /// The password-encrypted [`RootSeed`] which can be backed up in
107 /// GDrive.
108 /// - Applicable only in staging/prod.
109 /// - If Drive backup is not setup, instance will back up this encrypted
110 /// [`RootSeed`] in Google Drive. If a backup already exists, it is
111 /// overwritten.
112 /// - We require the client to password-encrypt prior to sending the
113 /// provision request to prevent leaking the length of the password. It
114 /// also shifts the burden of running the 600K HMAC iterations from the
115 /// provision instance to the mobile app.
116 #[serde(with = "hexstr_or_bytes")]
117 pub encrypted_seed: Vec<u8>,
118}
119// --- Channel Management --- //
120
121#[derive(Serialize, Deserialize)]
122pub struct ListChannelsResponse {
123 pub channels: Vec<LxChannelDetails>,
124}
125
126/// The information required for the user node to open a channel to the LSP.
127#[derive(Serialize, Deserialize)]
128pub struct OpenChannelRequest {
129 /// A user-provided id for this channel that's associated with the channel
130 /// throughout its whole lifetime, as the Lightning protocol channel id is
131 /// only known after negotiating the channel and creating the funding tx.
132 ///
133 /// This id is also used for idempotency. Retrying a request with the same
134 /// `user_channel_id` won't accidentally open another channel.
135 pub user_channel_id: LxUserChannelId,
136 /// The value of the channel we want to open.
137 pub value: Amount,
138}
139
140#[derive(Debug, Serialize, Deserialize)]
141pub struct OpenChannelResponse {
142 /// The Lightning protocol channel id of the newly created channel.
143 pub channel_id: LxChannelId,
144}
145
146#[derive(Serialize, Deserialize)]
147pub struct PreflightOpenChannelRequest {
148 /// The value of the channel we want to open.
149 pub value: Amount,
150}
151
152#[derive(Debug, Serialize, Deserialize)]
153pub struct PreflightOpenChannelResponse {
154 /// The estimated on-chain fee required to execute the channel open.
155 pub fee_estimate: Amount,
156}
157
158#[derive(Serialize, Deserialize)]
159pub struct CloseChannelRequest {
160 /// The id of the channel we want to close.
161 pub channel_id: LxChannelId,
162 /// Set to true if the channel should be force closed (unilateral).
163 /// Set to false if the channel should be cooperatively closed (bilateral).
164 pub force_close: bool,
165 /// The [`NodePk`] of our counterparty.
166 ///
167 /// If set to [`None`], the counterparty's [`NodePk`] will be determined by
168 /// calling [`list_channels`]. Setting this to [`Some`] allows
169 /// `close_channel` to avoid this relatively expensive [`Vec`] allocation.
170 ///
171 /// [`list_channels`]: lightning::ln::channelmanager::ChannelManager::list_channels
172 pub maybe_counterparty: Option<NodePk>,
173}
174
175pub type PreflightCloseChannelRequest = CloseChannelRequest;
176
177#[derive(Serialize, Deserialize)]
178pub struct PreflightCloseChannelResponse {
179 /// The estimated on-chain fee required to execute the channel close.
180 pub fee_estimate: Amount,
181}
182
183// --- Syncing and updating payments data --- //
184
185/// Upgradeable API struct for a [`PaymentId`].
186#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
187#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
188pub struct PaymentIdStruct {
189 /// The id of the payment to be fetched.
190 pub id: PaymentId,
191}
192
193/// An upgradeable version of [`Vec<PaymentId>`].
194#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
195#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
196pub struct VecPaymentId {
197 pub ids: Vec<PaymentId>,
198}
199
200/// Upgradeable API struct for a payment index.
201#[derive(Debug, PartialEq, Serialize, Deserialize)]
202#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
203pub struct PaymentCreatedIndexStruct {
204 /// The index of the payment to be fetched.
205 pub index: PaymentCreatedIndex,
206}
207
208/// Sync a batch of new payments to local storage.
209/// Results are returned in ascending `(created_at, payment_id)` order.
210#[derive(Debug, PartialEq, Serialize, Deserialize)]
211#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
212pub struct GetNewPayments {
213 /// Optional [`PaymentCreatedIndex`] at which the results should start,
214 /// exclusive. Payments with an index less than or equal to this will
215 /// not be returned.
216 pub start_index: Option<PaymentCreatedIndex>,
217 /// (Optional) the maximum number of results that can be returned.
218 pub limit: Option<u16>,
219}
220
221/// Get a batch of payments in ascending `(updated_at, payment_id)` order.
222#[derive(Debug, PartialEq, Serialize, Deserialize)]
223#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
224pub struct GetUpdatedPayments {
225 /// `(updated_at, id)` index at which the results should start, exclusive.
226 /// Payments with an index less than or equal to this will not be returned.
227 pub start_index: Option<PaymentUpdatedIndex>,
228 /// (Optional) the maximum number of results that can be returned.
229 pub limit: Option<u16>,
230}
231
232/// Get a batch of payment metadata in asc `(updated_at, payment_id)` order.
233#[derive(Debug, PartialEq, Serialize, Deserialize)]
234#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
235pub struct GetUpdatedPaymentMetadata {
236 /// `(updated_at, id)` index at which the results should start, exclusive.
237 /// Metadata with an index less than or equal to this will not be returned.
238 pub start_index: Option<PaymentUpdatedIndex>,
239 /// (Optional) the maximum number of results that can be returned.
240 pub limit: Option<u16>,
241}
242
243/// Upgradeable API struct for a list of [`PaymentCreatedIndex`]s.
244#[derive(Debug, PartialEq, Serialize, Deserialize)]
245#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
246pub struct PaymentCreatedIndexes {
247 /// The string-serialized [`PaymentCreatedIndex`]s of the payments to be
248 /// fetched. Typically, the ids passed here correspond to payments that
249 /// the mobile client currently has stored locally as "pending"; the
250 /// goal is to check whether any of these payments have been updated.
251 pub indexes: Vec<PaymentCreatedIndex>,
252}
253
254/// A request to update the personal note on a payment. Pass `None` to clear.
255#[derive(Clone, Serialize, Deserialize)]
256pub struct UpdatePaymentNote {
257 /// The index of the payment whose note should be updated.
258 // TODO(max): The server side only needs the `PaymentId`.
259 // This API should be changed to pass that instead.
260 pub index: PaymentCreatedIndex,
261 /// The updated note, or `None` to clear.
262 pub note: Option<BoundedNote>,
263}
264
265// --- BOLT11 Invoice Payments --- //
266
267#[derive(Default, Serialize, Deserialize)]
268pub struct CreateInvoiceRequest {
269 pub expiry_secs: u32,
270 pub amount: Option<Amount>,
271 /// The description to be encoded into the invoice.
272 ///
273 /// If `None`, the `description` field inside the invoice will be an empty
274 /// string (""), as lightning _requires_ a description (or description
275 /// hash) to be set.
276 /// NOTE: If both `description` and `description_hash` are set, node will
277 /// return an error.
278 pub description: Option<String>,
279 /// A 256-bit hash. Commonly a hash of a long description.
280 ///
281 /// This field is used to associate description longer than 639 bytes to
282 /// the invoice. Also known as '`h` tag in BOLT11'.
283 ///
284 /// This field is required to build invoices for the LNURL (LUD06)
285 /// receiving flow. Not used in other flows.
286 /// NOTE: If both `description` and `description_hash` are set, node will
287 /// return an error.
288 pub description_hash: Option<[u8; 32]>,
289 /// An optional note from the payer, stored with this inbound payment.
290 /// For LNURL-pay, set from the LUD-12 `comment`.
291 pub payer_note: Option<BoundedNote>,
292}
293
294#[derive(Serialize, Deserialize)]
295pub struct CreateInvoiceResponse {
296 pub invoice: Invoice,
297 /// The [`PaymentCreatedIndex`] of the newly created invoice payment.
298 ///
299 /// Is always `Some` starting at `node-v0.8.10` and `lsp-v0.8.11`.
300 //
301 // TODO(max): Make non-Option once all servers are sufficiently upgraded.
302 pub created_index: Option<PaymentCreatedIndex>,
303}
304
305#[derive(Serialize, Deserialize)]
306pub struct PayInvoiceRequest {
307 /// The invoice we want to pay.
308 pub invoice: Invoice,
309 /// Specifies the amount we will pay if the invoice to be paid is
310 /// amountless. This field must be [`Some`] for amountless invoices.
311 pub fallback_amount: Option<Amount>,
312 /// An optional personal note for this payment, useful if the
313 /// receiver-provided description is insufficient.
314 pub note: Option<BoundedNote>,
315 /// An optional payer note to persist with this outbound payment. For
316 /// LNURL-pay, this is the LUD-12 `comment` sent during invoice
317 /// negotiation.
318 pub payer_note: Option<BoundedNote>,
319}
320
321#[derive(Serialize, Deserialize)]
322pub struct PayInvoiceResponse {
323 /// When the node registered this payment.
324 /// Used in the [`PaymentCreatedIndex`].
325 pub created_at: TimestampMs,
326}
327
328#[derive(Serialize, Deserialize)]
329pub struct PreflightPayInvoiceRequest {
330 /// The invoice we want to pay.
331 pub invoice: Invoice,
332 /// Specifies the amount we will pay if the invoice to be paid is
333 /// amountless. This field must be [`Some`] for amountless invoices.
334 pub fallback_amount: Option<Amount>,
335}
336
337#[derive(Serialize, Deserialize)]
338pub struct PreflightPayInvoiceResponse {
339 /// The total amount to-be-paid for the pre-flighted [`Invoice`],
340 /// excluding the fees.
341 ///
342 /// This value may be different from the value originally requested if
343 /// we had to reach `htlc_minimum_msat` for some intermediate hops.
344 pub amount: Amount,
345 /// The total amount of fees to-be-paid for the pre-flighted [`Invoice`].
346 pub fees: Amount,
347 /// The route this invoice will be paid over.
348 // Added in node,lsp-v0.7.8
349 // TODO(max): We don't actually pay over this route.
350 pub route: LxRoute,
351}
352
353// --- BOLT12 Offer payments --- //
354
355#[derive(Default, Serialize, Deserialize)]
356pub struct CreateOfferRequest {
357 pub expiry_secs: Option<u32>,
358 /// The `amount` we're requesting for payments using this offer.
359 ///
360 /// If `None`, the offer is variable amount and the payer can choose any
361 /// value.
362 ///
363 /// If `Some`, the offer amount is fixed and the payer must pay exactly
364 /// this value (per item, see `max_quantity`).
365 pub amount: Option<Amount>,
366 /// The description to be encoded into the invoice.
367 ///
368 /// If `None`, the `description` field inside the invoice will be an empty
369 /// string (""), as lightning _requires_ a description to be set.
370 pub description: Option<String>,
371 /// The max number of items that can be purchased in any one payment for
372 /// the offer.
373 ///
374 /// NOTE: this is not related to single-use vs reusable offers.
375 ///
376 /// The expected amount paid for this offer is `offer.amount * quantity`,
377 /// where `offer.amount` is the value per item and `quantity` is the number
378 /// of items chosen _by the payer_. The payer's chosen `quantity` must be
379 /// in the range: `0 < quantity <= offer.max_quantity`.
380 ///
381 /// If `None`, defaults to `MaxQuantity::ONE`, i.e., the expected paid
382 /// `amount` is just `offer.amount`.
383 pub max_quantity: Option<MaxQuantity>,
384 /// The issuer of the offer.
385 ///
386 /// If `Some`, offer will encode the string. Bolt12 spec expects this tring
387 /// to be a domain or a `user@domain` address.
388 /// If `None`, offer issuer will encode "lexe.app" as the issuer.
389 pub issuer: Option<String>,
390 //
391 // TODO(phlip9): add a `single_use` field to the offer request? right now
392 // all offers are reusable.
393}
394
395#[derive(Serialize, Deserialize)]
396pub struct CreateOfferResponse {
397 pub offer: Offer,
398}
399
400#[derive(Serialize, Deserialize)]
401pub struct PreflightPayOfferRequest {
402 /// The user-provided idempotency id for this payment.
403 pub cid: ClientPaymentId,
404 /// The offer we want to pay.
405 pub offer: Offer,
406 /// Specifies the amount we will pay if the offer to be paid is
407 /// amountless. This field must be [`Some`] for amountless offers.
408 pub fallback_amount: Option<Amount>,
409}
410
411#[derive(Serialize, Deserialize)]
412pub struct PreflightPayOfferResponse {
413 /// The total amount to-be-paid for the pre-flighted [`Offer`],
414 /// excluding the fees.
415 ///
416 /// This value may be different from the value originally requested if
417 /// we had to reach `htlc_minimum_msat` for some intermediate hops.
418 pub amount: Amount,
419 /// The total amount of fees to-be-paid for the pre-flighted [`Offer`].
420 ///
421 /// Since we only approximate the route atm, we likely underestimate the
422 /// actual fee.
423 pub fees: Amount,
424 /// The route this offer will be paid over.
425 ///
426 /// Because we don't yet fetch the actual BOLT 12 invoice during preflight,
427 /// this route is only an approximation of the final route (we can only
428 /// route to the last public node before the offer's blinded path begins).
429 // Added in node,lsp-v0.7.8
430 // TODO(max): We don't actually pay over this route.
431 pub route: LxRoute,
432}
433
434#[derive(Serialize, Deserialize)]
435pub struct PayOfferRequest {
436 /// The user-provided idempotency id for this payment.
437 pub cid: ClientPaymentId,
438 /// The offer we want to pay.
439 pub offer: Offer,
440 /// Specifies the amount we will pay if the offer to be paid is
441 /// amountless. This field must be [`Some`] for amountless offers.
442 pub fallback_amount: Option<Amount>,
443 /// An optional personal note for this payment, useful if the
444 /// receiver-provided description is insufficient.
445 pub note: Option<BoundedNote>,
446 /// An optional note included in the BOLT12 invoice request and visible to
447 /// the recipient.
448 pub payer_note: Option<BoundedNote>,
449}
450
451#[derive(Serialize, Deserialize)]
452pub struct PayOfferResponse {
453 /// When the node registered this payment. Used in the
454 /// [`PaymentCreatedIndex`].
455 pub created_at: TimestampMs,
456}
457
458// --- On-chain payments --- //
459
460#[derive(Debug, PartialEq, Serialize, Deserialize)]
461#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
462pub struct GetAddressResponse {
463 #[cfg_attr(
464 any(test, feature = "test-utils"),
465 proptest(strategy = "arbitrary::any_mainnet_addr_unchecked()")
466 )]
467 pub addr: bitcoin::Address<NetworkUnchecked>,
468}
469
470#[derive(Serialize, Deserialize)]
471#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary, Debug))]
472pub struct PayOnchainRequest {
473 /// The identifier to use for this payment.
474 pub cid: ClientPaymentId,
475 /// The address we want to send funds to.
476 #[cfg_attr(
477 any(test, feature = "test-utils"),
478 proptest(strategy = "arbitrary::any_mainnet_addr_unchecked()")
479 )]
480 pub address: bitcoin::Address<NetworkUnchecked>,
481 /// How much Bitcoin we want to send.
482 pub amount: Amount,
483 /// How quickly we want our transaction to be confirmed.
484 /// The higher the priority, the more fees we will pay.
485 // See LexeEsplora for the conversion to the target number of blocks
486 pub priority: ConfirmationPriority,
487 /// An optional personal note for this payment.
488 pub note: Option<BoundedNote>,
489}
490
491#[derive(Serialize, Deserialize)]
492pub struct PayOnchainResponse {
493 /// When the node registered this payment. Used in the
494 /// [`PaymentCreatedIndex`].
495 pub created_at: TimestampMs,
496 /// The Bitcoin txid for the transaction we just submitted to the mempool.
497 pub txid: Txid,
498}
499
500#[derive(Debug, PartialEq, Serialize, Deserialize)]
501pub struct PreflightPayOnchainRequest {
502 /// The address we want to send funds to.
503 pub address: bitcoin::Address<NetworkUnchecked>,
504 /// How much Bitcoin we want to send.
505 pub amount: Amount,
506}
507
508#[derive(Serialize, Deserialize)]
509pub struct PreflightPayOnchainResponse {
510 /// Corresponds with [`ConfirmationPriority::High`]
511 ///
512 /// The high estimate is optional--we don't want to block the user from
513 /// sending if they only have enough for a normal tx fee.
514 pub high: Option<FeeEstimate>,
515 /// Corresponds with [`ConfirmationPriority::Normal`]
516 pub normal: FeeEstimate,
517 /// Corresponds with [`ConfirmationPriority::Background`]
518 pub background: FeeEstimate,
519}
520
521#[derive(Serialize, Deserialize)]
522pub struct FeeEstimate {
523 /// The fee amount estimate.
524 pub amount: Amount,
525}
526
527// --- Sync --- //
528
529#[derive(Serialize, Deserialize)]
530pub struct ResyncRequest {
531 /// If true, the LSP will full sync the BDK wallet and do a normal LDK
532 /// sync.
533 pub full_sync: bool,
534}
535
536// --- Username --- //
537
538/// Creates or updates a human Bitcoin address.
539#[derive(Debug, PartialEq, Serialize, Deserialize)]
540#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
541pub struct UpdateHumanBitcoinAddress {
542 /// Username for BIP-353 and LNURL.
543 pub username: Username,
544 /// Offer to be used to fetch invoices on BIP-353.
545 pub offer: Offer,
546}
547
548/// Claims a generated human Bitcoin address.
549///
550/// This endpoint is used during node initialization to claim an auto-generated
551/// human Bitcoin address. The address will have `is_primary: false` and
552/// `is_generated: true`.
553#[derive(Debug, PartialEq, Serialize, Deserialize)]
554#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
555pub struct ClaimGeneratedHumanBitcoinAddress {
556 /// Offer to be used to fetch invoices on BIP-353.
557 pub offer: Offer,
558 /// The username to claim. This must be the username returned by
559 /// `get_generated_username`.
560 pub username: Username,
561}
562
563/// Response for `get_generated_username` endpoint.
564#[derive(Debug, PartialEq, Serialize, Deserialize)]
565#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
566pub struct GetGeneratedUsernameResponse {
567 /// The generated username that can be used for claiming an HBA.
568 pub username: Username,
569 /// Whether this user already has a claimed generated HBA.
570 /// If true, the caller should skip calling
571 /// `claim_generated_human_bitcoin_address`.
572 pub already_claimed: bool,
573}
574
575#[derive(Debug, PartialEq, Serialize, Deserialize)]
576#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
577pub struct HumanBitcoinAddress {
578 /// Current username for BIP-353 and LNURL.
579 pub username: Option<Username>,
580 /// Current offer for fetching invoices on BIP-353.
581 pub offer: Option<Offer>,
582 /// Last time the human Bitcoin address was updated.
583 pub updated_at: Option<TimestampMs>,
584 /// Whether the human Bitcoin address can be updated. Always `true` for
585 /// generated addresses; for claimed addresses, depends on time-based
586 /// freeze rules.
587 pub updatable: bool,
588}
589
590#[cfg(any(test, feature = "test-utils"))]
591mod arbitrary_impl {
592 use proptest::{
593 arbitrary::{Arbitrary, any},
594 strategy::{BoxedStrategy, Strategy},
595 };
596
597 use super::*;
598
599 impl Arbitrary for PreflightPayOnchainRequest {
600 type Parameters = ();
601 type Strategy = BoxedStrategy<Self>;
602
603 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
604 (arbitrary::any_mainnet_addr_unchecked(), any::<Amount>())
605 .prop_map(|(address, amount)| Self { address, amount })
606 .boxed()
607 }
608 }
609}
610
611#[cfg(test)]
612mod test {
613 use lexe_common::test_utils::roundtrip::{
614 self, query_string_roundtrip_proptest,
615 };
616
617 use super::*;
618
619 #[test]
620 fn preflight_pay_onchain_roundtrip() {
621 query_string_roundtrip_proptest::<PreflightPayOnchainRequest>();
622 }
623
624 #[test]
625 fn payment_id_struct_roundtrip() {
626 query_string_roundtrip_proptest::<PaymentIdStruct>();
627 }
628
629 #[test]
630 fn payment_index_struct_roundtrip() {
631 query_string_roundtrip_proptest::<PaymentCreatedIndexStruct>();
632 }
633
634 #[test]
635 fn get_new_payments_roundtrip() {
636 query_string_roundtrip_proptest::<GetNewPayments>();
637 }
638
639 #[test]
640 fn payment_indexes_roundtrip() {
641 // This is serialized as JSON, not query strings.
642 roundtrip::json_value_roundtrip_proptest::<PaymentCreatedIndexes>();
643 }
644
645 #[test]
646 fn get_address_response_roundtrip() {
647 roundtrip::json_value_roundtrip_proptest::<GetAddressResponse>();
648 }
649
650 #[test]
651 fn setup_gdrive_request_roundtrip() {
652 roundtrip::json_string_roundtrip_proptest::<SetupGDrive>();
653 }
654
655 #[test]
656 fn human_bitcoin_address_request_roundtrip() {
657 roundtrip::json_value_roundtrip_proptest::<UpdateHumanBitcoinAddress>();
658 }
659
660 #[test]
661 fn claim_generated_human_bitcoin_address_request_roundtrip() {
662 roundtrip::json_value_roundtrip_proptest::<
663 ClaimGeneratedHumanBitcoinAddress,
664 >();
665 }
666
667 #[test]
668 fn get_generated_username_response_roundtrip() {
669 roundtrip::json_value_roundtrip_proptest::<GetGeneratedUsernameResponse>(
670 );
671 }
672
673 #[test]
674 fn human_bitcoin_address_response_roundtrip() {
675 roundtrip::json_value_roundtrip_proptest::<HumanBitcoinAddress>();
676 }
677}