Skip to main content

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}