uniffi_lipalightninglib/lightning/
bolt11.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use crate::amount::AsSats;
use crate::errors::map_send_payment_error;
use crate::locker::Locker;
use crate::support::Support;
use crate::{
    InvoiceCreationMetadata, InvoiceDetails, PayErrorCode, PayResult, PaymentMetadata,
    RuntimeErrorCode,
};
use breez_sdk_core::error::SendPaymentError;
use breez_sdk_core::{OpeningFeeParams, SendPaymentRequest};
use perro::{ensure, runtime_error, MapToError};
use std::sync::Arc;

pub struct Bolt11 {
    support: Arc<Support>,
}

impl Bolt11 {
    pub(crate) fn new(support: Arc<Support>) -> Self {
        Self { support }
    }

    /// Create a bolt11 invoice to receive a payment with.
    ///
    /// Parameters:
    /// * `amount_sat` - the smallest amount of sats required for the node to accept the incoming
    ///   payment (sender will have to pay fees on top of that amount)
    /// * `lsp_fee_params` - the params that will be used to determine the lsp fee.
    ///    Can be obtained from [`Lightning::calculate_lsp_fee_for_amount`](crate::Lightning::calculate_lsp_fee_for_amount)
    ///    to guarantee predicted fees are the ones charged.
    /// * `description` - a description to be embedded into the created invoice
    /// * `metadata` - additional data about the invoice creation used for analytics purposes,
    ///    used to improve the user experience
    ///
    /// Requires network: **yes**
    pub fn create(
        &self,
        amount_sat: u64,
        lsp_fee_params: Option<OpeningFeeParams>,
        description: String,
        metadata: InvoiceCreationMetadata,
    ) -> crate::Result<InvoiceDetails> {
        let response = self
            .support
            .rt
            .handle()
            .block_on(
                self.support
                    .sdk
                    .receive_payment(breez_sdk_core::ReceivePaymentRequest {
                        amount_msat: amount_sat.as_sats().msats,
                        description,
                        preimage: None,
                        opening_fee_params: lsp_fee_params,
                        use_description_hash: None,
                        expiry: None,
                        cltv: None,
                    }),
            )
            .map_to_runtime_error(
                RuntimeErrorCode::NodeUnavailable,
                "Failed to create an invoice",
            )?;

        self.support
            .store_payment_info(&response.ln_invoice.payment_hash, None);
        self.support
            .data_store
            .lock_unwrap()
            .store_created_invoice(
                &response.ln_invoice.payment_hash,
                &response.ln_invoice.bolt11,
                &response.opening_fee_msat,
                response.ln_invoice.timestamp + response.ln_invoice.expiry,
            )
            .map_to_permanent_failure("Failed to persist created invoice")?;

        self.support.analytics_interceptor.request_initiated(
            response.clone(),
            self.support.get_exchange_rate(),
            metadata,
        );
        Ok(InvoiceDetails::from_ln_invoice(
            response.ln_invoice,
            &self.support.get_exchange_rate(),
        ))
    }

    /// Start an attempt to pay an invoice. Can immediately fail, meaning that the payment couldn't be started.
    /// If successful, it doesn't mean that the payment itself was successful (funds received by the payee).
    /// After this method returns, the consumer of this library will learn about a successful/failed payment through the
    /// callbacks [`EventsCallback::payment_sent`](crate::EventsCallback::payment_sent) and
    /// [`EventsCallback::payment_failed`](crate::EventsCallback::payment_failed).
    ///
    /// Parameters:
    /// * `invoice_details` - details of an invoice decode by [`LightningNode::decode_data`](crate::LightningNode::decode_data)
    /// * `metadata` - additional meta information about the payment, used by analytics to improve the user experience.
    ///
    /// Requires network: **yes**
    pub fn pay(&self, invoice_details: InvoiceDetails, metadata: PaymentMetadata) -> PayResult<()> {
        self.pay_open_amount(invoice_details, 0, metadata)
    }

    /// Similar to [`Bolt11::pay`] with the difference that the passed in invoice
    /// does not have any payment amount specified, and allows the caller of the method to
    /// specify an amount instead.
    ///
    /// Additional Parameters:
    /// * `amount_sat` - amount in sats to be paid
    ///
    /// Requires network: **yes**
    pub fn pay_open_amount(
        &self,
        invoice_details: InvoiceDetails,
        amount_sat: u64,
        metadata: PaymentMetadata,
    ) -> PayResult<()> {
        let amount_msat = if amount_sat == 0 {
            None
        } else {
            Some(amount_sat.as_sats().msats)
        };
        self.support
            .store_payment_info(&invoice_details.payment_hash, None);
        let node_state = self
            .support
            .sdk
            .node_info()
            .map_to_runtime_error(PayErrorCode::NodeUnavailable, "Failed to read node info")?;
        ensure!(
            node_state.id != invoice_details.payee_pub_key,
            runtime_error(
                PayErrorCode::PayingToSelf,
                "A locally issued invoice tried to be paid"
            )
        );

        self.support.analytics_interceptor.pay_initiated(
            invoice_details.clone(),
            metadata,
            amount_msat,
            self.support.get_exchange_rate(),
        );

        let result = self
            .support
            .rt
            .handle()
            .block_on(self.support.sdk.send_payment(SendPaymentRequest {
                bolt11: invoice_details.invoice,
                use_trampoline: true,
                amount_msat,
                label: None,
            }));

        if matches!(
            result,
            Err(SendPaymentError::Generic { .. }
                | SendPaymentError::PaymentFailed { .. }
                | SendPaymentError::PaymentTimeout { .. }
                | SendPaymentError::RouteNotFound { .. }
                | SendPaymentError::RouteTooExpensive { .. }
                | SendPaymentError::ServiceConnectivity { .. })
        ) {
            self.support
                .report_send_payment_issue(invoice_details.payment_hash);
        }

        result.map_err(map_send_payment_error)?;
        Ok(())
    }
}