vls_protocol_signer/
approver.rs

1use bitcoin::secp256k1::{PublicKey, SecretKey};
2use bitcoin::{Transaction, TxOut};
3use lightning_signer::bitcoin;
4use lightning_signer::prelude::*;
5use log::*;
6
7use lightning_signer::invoice::{Invoice, InvoiceAttributes};
8use lightning_signer::lightning::types::payment::PaymentHash;
9use lightning_signer::node::Node;
10use lightning_signer::policy::error::ValidationErrorKind;
11use lightning_signer::util::clock::Clock;
12use lightning_signer::util::debug_utils::DebugBytes;
13use lightning_signer::util::status::Status;
14use lightning_signer::util::velocity::VelocityControl;
15use lightning_signer::wallet::Wallet;
16
17/// Control payment approval.
18///
19/// You should implement this to present a user interface to allow the user to
20/// approve or reject payments.
21/// An approval here is meant to override other controls, such as the node allowlist.
22///
23/// This can also be used for automatic approval of micropayments to arbitrary destinations
24/// - see [`VelocityApprover`].
25///
26/// Implement the `approve_invoice`, `approve_keysend` and `approve_onchain` methods to
27/// control which payments are approved.
28///
29/// The flow is as follows:
30/// - TODO the node allowlist is consulted, and the payment is approved if there is a match
31/// - if an L2 payment was previously approved, it is automatically approved again
32/// - the approver is consulted, and the payment is rejected if false was returned
33/// - the global node velocity control is consulted if configured,
34///   and the payment is rejected if the velocity is exceeded
35/// - otherwise, the payment is approved
36pub trait Approve: SendSync {
37    /// Approve an invoice for payment
38    fn approve_invoice(&self, invoice: &Invoice) -> bool;
39
40    /// Approve a keysend (ad-hoc payment)
41    fn approve_keysend(&self, payment_hash: PaymentHash, amount_msat: u64) -> bool;
42
43    /// Approve an onchain payment to an unknown destination
44    /// * `tx` - the transaction to be sent
45    /// * `prev_outs` - the previous outputs used as inputs for this tx
46    /// * `unknown_indices` is the list of tx output indices that are unknown.
47    fn approve_onchain(
48        &self,
49        tx: &Transaction,
50        prev_outs: &[TxOut],
51        unknown_indices: &[usize],
52    ) -> bool;
53
54    /// Checks invoice for approval and adds to the node if needed and appropriate
55    fn handle_proposed_invoice(&self, node: &Arc<Node>, invoice: Invoice) -> Result<bool, Status> {
56        let (payment_hash, _payment_state, invoice_hash) =
57            Node::payment_state_from_invoice(&invoice)?;
58
59        // shortcut if node already has this invoice
60        if node.has_payment(&payment_hash, &invoice_hash)? {
61            debug!(
62                "node already approved invoice with payment_hash {:?} invoice_hash {:?}",
63                DebugBytes(&payment_hash.0),
64                DebugBytes(&invoice_hash)
65            );
66            return Ok(true);
67        }
68
69        // otherwise ask approver
70        let payee = invoice.payee_pub_key();
71        if node.allowlist_contains_payee(payee) {
72            debug!(
73                "node allowlist contains payee {:?} for invoice with amount {}",
74                payee,
75                invoice.amount_milli_satoshis()
76            );
77            node.add_invoice(invoice)
78        } else if self.approve_invoice(&invoice) {
79            debug!(
80                "invoice to {:?} approved with amount {}",
81                payee,
82                invoice.amount_milli_satoshis()
83            );
84            node.add_invoice(invoice)
85        } else {
86            warn!(
87                "invoice to {:?} not approved with amount {}",
88                payee,
89                invoice.amount_milli_satoshis()
90            );
91            Ok(false)
92        }
93    }
94
95    /// Checks keysend for approval and adds to the node if needed and appropriate.
96    /// The payee is not validated yet.
97    fn handle_proposed_keysend(
98        &self,
99        node: &Arc<Node>,
100        payee: PublicKey,
101        payment_hash: PaymentHash,
102        amount_msat: u64,
103    ) -> Result<bool, Status> {
104        let now = node.get_clock().now();
105        let (_payment_state, invoice_hash) =
106            Node::payment_state_from_keysend(payee, payment_hash, amount_msat, now)?;
107
108        // shortcut if node already has this payment
109        if node.has_payment(&payment_hash, &invoice_hash)? {
110            debug!(
111                "node already approved keysend with payment_hash {:?} invoice_hash {:?}",
112                DebugBytes(&payment_hash.0),
113                DebugBytes(&invoice_hash)
114            );
115            return Ok(true);
116        }
117
118        // TODO when payee validated by by generating the onion ourselves check if payee public key
119        // in allowlist
120
121        // otherwise ask approver
122        if self.approve_keysend(payment_hash, amount_msat) {
123            debug!("keysend to {:?} approved with amount {}", payee, amount_msat);
124            node.add_keysend(payee, payment_hash, amount_msat).map_err(|err| {
125                warn!("add_keysend failed: {}", err);
126                err
127            })
128        } else {
129            warn!("keysend to {:?} not approved with amount {}", payee, amount_msat);
130            Ok(false)
131        }
132    }
133
134    /// Checks onchain payment for unknown destinations and checks approval
135    /// for any such outputs.
136    /// Returns Ok(false) if any unknown destinations were not approved.
137    fn handle_proposed_onchain(
138        &self,
139        node: &Arc<Node>,
140        tx: &Transaction,
141        segwit_flags: &[bool],
142        prev_outs: &[TxOut],
143        uniclosekeys: &[Option<(SecretKey, Vec<Vec<u8>>)>],
144        opaths: &[Vec<u32>],
145    ) -> Result<bool, Status> {
146        let check_result =
147            node.check_onchain_tx(&tx, segwit_flags, &prev_outs, &uniclosekeys, &opaths);
148        match check_result {
149            Ok(()) => {}
150            Err(ve) => match ve.kind {
151                ValidationErrorKind::UnknownDestinations(_, ref indices) => {
152                    if self.approve_onchain(&tx, &prev_outs, indices) {
153                        info!("approved onchain tx with unknown outputs");
154                    } else {
155                        info!("rejected onchain tx with unknown outputs");
156                        return Ok(false);
157                    }
158                }
159                _ => {
160                    return Err(Status::failed_precondition(ve.to_string()))?;
161                }
162            },
163        }
164        Ok(true)
165    }
166}
167
168/// An approver that always approves, for testing and for permissive mode.
169///
170/// NOTE - this version approves quietly, if the approval should be logged
171/// with a warning use [`WarningPositiveApprover`] instead.
172#[derive(Copy, Clone)]
173pub struct PositiveApprover();
174
175impl SendSync for PositiveApprover {}
176
177impl Approve for PositiveApprover {
178    fn approve_invoice(&self, _invoice: &Invoice) -> bool {
179        true
180    }
181
182    fn approve_keysend(&self, _payment_hash: PaymentHash, _amount_msat: u64) -> bool {
183        true
184    }
185
186    fn approve_onchain(
187        &self,
188        _tx: &Transaction,
189        _prev_outs: &[TxOut],
190        _unknown_indices: &[usize],
191    ) -> bool {
192        true
193    }
194}
195
196/// An approver that always approves, for testing and for permissive mode.
197///
198/// NOTE - this version generates a warning to the log so the user is aware
199/// of the automatic approvals.
200#[derive(Copy, Clone)]
201pub struct WarningPositiveApprover();
202
203impl SendSync for WarningPositiveApprover {}
204
205impl Approve for WarningPositiveApprover {
206    fn approve_invoice(&self, invoice: &Invoice) -> bool {
207        warn!("AUTOAPPROVED INVOICE {:?}", invoice);
208        true
209    }
210
211    fn approve_keysend(&self, payment_hash: PaymentHash, amount_msat: u64) -> bool {
212        warn!(
213            "AUTOAPPROVED KEYSEND of {} msat with payment_hash {:?}",
214            amount_msat,
215            DebugBytes(&payment_hash.0)
216        );
217        true
218    }
219
220    fn approve_onchain(
221        &self,
222        tx: &Transaction,
223        prev_outs: &[TxOut],
224        unknown_indices: &[usize],
225    ) -> bool {
226        warn!(
227            "AUTOAPPROVED ONCHAIN tx {:?} with values_sat {:?} and unknown_indices {:?}",
228            tx, prev_outs, unknown_indices
229        );
230        true
231    }
232}
233
234/// An approver that always declines, in case only the allowlist is used
235#[derive(Copy, Clone)]
236pub struct NegativeApprover();
237
238impl SendSync for NegativeApprover {}
239
240impl Approve for NegativeApprover {
241    fn approve_invoice(&self, _invoice: &Invoice) -> bool {
242        false
243    }
244
245    fn approve_keysend(&self, _payment_hash: PaymentHash, _amount_msat: u64) -> bool {
246        false
247    }
248
249    fn approve_onchain(
250        &self,
251        _tx: &Transaction,
252        _prev_outs: &[TxOut],
253        _unknown_indices: &[usize],
254    ) -> bool {
255        false
256    }
257}
258
259/// An approver that auto-approves L2 payments under a certain velocity.
260/// If the invoice is over the velocity, it is passed on to a delegate approver.
261/// You can use this to allow micropayments to arbitrary destinations.
262///
263/// You can also pass in a delegate approver, to allow asking the user
264/// for approval for payments over the micropayment maximum velocity.
265///
266/// L1 payments are always passed to the delegate approver (i.e. velocity control
267/// is not used for approval).
268///
269/// ```rust
270/// # use std::sync::Arc;
271/// # use std::time::Duration;
272/// use lightning_signer::util::clock::ManualClock;
273/// use lightning_signer::util::velocity::{
274///     VelocityControl,
275///     VelocityControlIntervalType::Hourly,
276///     VelocityControlSpec
277/// };
278/// # use vls_protocol_signer::approver::{NegativeApprover, VelocityApprover};
279///
280/// let delegate = NegativeApprover();
281/// let clock = Arc::new(ManualClock::new(Duration::ZERO));
282/// let spec = VelocityControlSpec {
283///     limit_msat: 1000000,
284///     interval_type: Hourly
285/// };
286/// let control = VelocityControl::new(spec);
287/// let approver = VelocityApprover::new(clock.clone(), control, delegate);
288/// let state = approver.control().get_state();
289/// // persist the state here if you don't want the velocity control to be cleared
290/// // every time the signer restarts
291/// // ...
292/// // now restore from the state
293/// let restored_control = VelocityControl::load_from_state(spec, state);
294/// let restored_approver = VelocityApprover::new(clock.clone(), restored_control, delegate);
295/// ```
296pub struct VelocityApprover<A: Approve> {
297    clock: Arc<dyn Clock>,
298    control: Mutex<VelocityControl>,
299    delegate: A,
300}
301
302impl<A: Approve> VelocityApprover<A> {
303    /// Create a new velocity approver with the given velocity control and delgate approver
304    pub fn new(clock: Arc<dyn Clock>, control: VelocityControl, delegate: A) -> Self {
305        Self { control: Mutex::new(control), clock, delegate }
306    }
307
308    /// Get a snapshot of the velocity control, for persistence
309    pub fn control(&self) -> VelocityControl {
310        self.control.lock().unwrap().clone()
311    }
312
313    /// Set the velocity control
314    pub fn set_control(&self, control: VelocityControl) {
315        *self.control.lock().unwrap() = control;
316    }
317}
318
319impl<A: Approve> SendSync for VelocityApprover<A> {}
320
321impl<A: Approve> Approve for VelocityApprover<A> {
322    fn approve_invoice(&self, invoice: &Invoice) -> bool {
323        let mut control = self.control.lock().unwrap();
324        let success = control.insert(self.clock.now().as_secs(), invoice.amount_milli_satoshis());
325        if success {
326            true
327        } else {
328            let success = self.delegate.approve_invoice(invoice);
329            if success {
330                // since we got a manual approval, clear the control, so that we
331                // don't bother the user until more transactions flow through
332                control.clear();
333            }
334            success
335        }
336    }
337
338    fn approve_keysend(&self, payment_hash: PaymentHash, amount_msat: u64) -> bool {
339        let mut control = self.control.lock().unwrap();
340        let success = control.insert(self.clock.now().as_secs(), amount_msat);
341        if success {
342            true
343        } else {
344            let success = self.delegate.approve_keysend(payment_hash, amount_msat);
345            if success {
346                // since we got a manual approval, clear the control, so that we
347                // don't bother the user until more transactions flow through
348                control.clear();
349            }
350            success
351        }
352    }
353
354    fn approve_onchain(
355        &self,
356        tx: &Transaction,
357        prev_outs: &[TxOut],
358        unknown_indices: &[usize],
359    ) -> bool {
360        self.delegate.approve_onchain(tx, prev_outs, unknown_indices)
361    }
362}
363
364/// An approval that is memorized by `MemoApprover`
365#[derive(Debug)]
366pub enum Approval {
367    /// An invoice was approved
368    Invoice(Invoice),
369    /// A keysend was approved
370    KeySend(PaymentHash, u64),
371    /// An onchain transaction was approved
372    Onchain(Transaction),
373}
374
375/// An approver that memorizes the last approval, and uses it for the next
376/// approval request.
377///
378/// If the request is for a different action, the memoized approval is cleared
379/// and the request is passed on to the delegate approver.
380pub struct MemoApprover<A: Approve> {
381    delegate: A,
382    approvals: Mutex<Vec<Approval>>,
383}
384
385impl<A: Approve> MemoApprover<A> {
386    /// Create a new memo approver with the given delegate approver
387    pub fn new(delegate: A) -> Self {
388        Self { delegate, approvals: Mutex::new(Vec::new()) }
389    }
390
391    /// Set an approval to be memorized.
392    ///
393    /// This approval will be used for the next approval request.
394    /// If there is already a memoized approval, it will be overwritten.
395    pub fn approve(&self, approvals: Vec<Approval>) {
396        *self.approvals.lock().unwrap() = approvals;
397    }
398}
399
400impl<A: Approve> SendSync for MemoApprover<A> {}
401
402impl<A: Approve> Approve for MemoApprover<A> {
403    fn approve_invoice(&self, invoice: &Invoice) -> bool {
404        let mut approvals = self.approvals.lock().unwrap();
405        for approval in approvals.drain(..) {
406            match approval {
407                Approval::Invoice(approved_invoice) => {
408                    if approved_invoice.invoice_hash() == invoice.invoice_hash() {
409                        return true;
410                    }
411                }
412                _ => {}
413            }
414        }
415        return self.delegate.approve_invoice(invoice);
416    }
417
418    fn approve_keysend(&self, payment_hash: PaymentHash, amount_msat: u64) -> bool {
419        let mut approvals = self.approvals.lock().unwrap();
420        for approval in approvals.drain(..) {
421            match approval {
422                Approval::KeySend(approved_payment_hash, approved_amount_msat) =>
423                    if approved_payment_hash == payment_hash && approved_amount_msat == amount_msat
424                    {
425                        return true;
426                    },
427                _ => {}
428            }
429        }
430        return self.delegate.approve_keysend(payment_hash, amount_msat);
431    }
432
433    fn approve_onchain(
434        &self,
435        tx: &Transaction,
436        prev_outs: &[TxOut],
437        unknown_indices: &[usize],
438    ) -> bool {
439        let mut approvals = self.approvals.lock().unwrap();
440        for approval in approvals.drain(..) {
441            match approval {
442                Approval::Onchain(approved_tx) =>
443                    if approved_tx == *tx {
444                        return true;
445                    },
446                _ => {}
447            }
448        }
449        return self.delegate.approve_onchain(tx, prev_outs, unknown_indices);
450    }
451}
452
453#[cfg(test)]
454mod tests {
455    use crate::approver::{
456        Approve, NegativeApprover, PositiveApprover, VelocityApprover, WarningPositiveApprover,
457    };
458    use lightning::types::payment::PaymentHash;
459    use lightning_signer::bitcoin::secp256k1::PublicKey;
460    use lightning_signer::invoice::InvoiceAttributes;
461    use lightning_signer::lightning;
462    use lightning_signer::node::{Node, PaymentState};
463    use lightning_signer::util::clock::Clock;
464    use lightning_signer::util::clock::ManualClock;
465    use lightning_signer::util::test_utils::{
466        make_current_test_invoice, make_node, make_test_invoice,
467    };
468    use lightning_signer::util::velocity::{
469        VelocityControl, VelocityControlIntervalType::Hourly, VelocityControlSpec,
470    };
471    use std::sync::Arc;
472    use std::time::Duration;
473    use test_log::test;
474
475    #[test]
476    fn test_invoice_velocity_approver_negative() {
477        let delegate = NegativeApprover();
478        let clock = Arc::new(ManualClock::new(Duration::ZERO));
479        let spec = VelocityControlSpec { limit_msat: 1_000_000, interval_type: Hourly };
480        let control = VelocityControl::new(spec);
481        let approver = VelocityApprover::new(clock.clone(), control, delegate);
482        let amt = 600_000_u64;
483        let invoice = make_test_invoice(1, amt);
484        let success = approver.approve_invoice(&invoice);
485        assert!(success);
486
487        let invoice = make_test_invoice(2, amt);
488        let success = approver.approve_invoice(&invoice);
489        assert!(!success);
490        assert_eq!(approver.control.lock().unwrap().velocity(), amt);
491    }
492
493    #[test]
494    fn test_handle_invoice_allowlist() {
495        // need a node for this test for the allowlist
496        let (_, node, _) = make_node();
497        let approver = NegativeApprover();
498        let invoice = make_current_test_invoice(1, 600_000);
499        assert!(!approver.handle_proposed_invoice(&node, invoice.clone()).unwrap());
500
501        let allowable = format!("payee:{}", invoice.payee_pub_key());
502        node.add_allowlist(&[allowable]).unwrap();
503        assert!(approver.handle_proposed_invoice(&node, invoice).unwrap());
504    }
505
506    #[test]
507    fn test_invoice_velocity_approver_positive() {
508        let delegate = PositiveApprover();
509        let clock = Arc::new(ManualClock::new(Duration::ZERO));
510        let spec = VelocityControlSpec { limit_msat: 1_000_000, interval_type: Hourly };
511        let control = VelocityControl::new(spec);
512        let approver = VelocityApprover::new(clock.clone(), control, delegate);
513        let amt = 600_000_u64;
514        let invoice = make_test_invoice(1, amt);
515        let success = approver.approve_invoice(&invoice);
516        assert!(success);
517        assert_eq!(approver.control.lock().unwrap().velocity(), amt);
518
519        let invoice = make_test_invoice(2, amt);
520        let success = approver.approve_invoice(&invoice);
521        assert!(success);
522        // the approval of the second invoice should have cleared the velocity control
523        assert_eq!(approver.control.lock().unwrap().velocity(), 0);
524    }
525
526    #[test]
527    fn test_keysend_velocity_approver_negative() {
528        let delegate = NegativeApprover();
529        let clock = Arc::new(ManualClock::new(Duration::ZERO));
530        let spec = VelocityControlSpec { limit_msat: 1000, interval_type: Hourly };
531        let control = VelocityControl::new(spec);
532        let approver = VelocityApprover::new(clock.clone(), control, delegate);
533        let (payment_hash, payment_state) = make_keysend_payment(1, clock.now());
534        let success = approver.approve_keysend(payment_hash, payment_state.amount_msat);
535        assert!(success);
536
537        let (payment_hash, payment_state) = make_keysend_payment(2, clock.now());
538        let success = approver.approve_keysend(payment_hash, payment_state.amount_msat);
539        assert!(!success);
540        assert_eq!(approver.control.lock().unwrap().velocity(), 600);
541    }
542
543    #[test]
544    fn test_keysend_velocity_approver_positive() {
545        let delegate = PositiveApprover();
546        let clock = Arc::new(ManualClock::new(Duration::ZERO));
547        let spec = VelocityControlSpec { limit_msat: 1000, interval_type: Hourly };
548        let control = VelocityControl::new(spec);
549        let approver = VelocityApprover::new(clock.clone(), control, delegate);
550        let (payment_hash, payment_state) = make_keysend_payment(1, clock.now());
551        let success = approver.approve_keysend(payment_hash, payment_state.amount_msat);
552        assert!(success);
553        assert_eq!(approver.control.lock().unwrap().velocity(), 600);
554
555        let (payment_hash, payment_state) = make_keysend_payment(2, clock.now());
556        let success = approver.approve_keysend(payment_hash, payment_state.amount_msat);
557        assert!(success);
558        // the approval of the second invoice should have cleared the velocity control
559        assert_eq!(approver.control.lock().unwrap().velocity(), 0);
560    }
561
562    fn make_keysend_payment(x: u8, now: Duration) -> (PaymentHash, PaymentState) {
563        let payee = PublicKey::from_slice(&[2u8; 33]).unwrap();
564        let payment_hash = PaymentHash([x; 32]);
565        let (payment_state, _invoice_hash) =
566            Node::payment_state_from_keysend(payee, payment_hash, 600, now).unwrap();
567        (payment_hash, payment_state)
568    }
569
570    #[test]
571    fn test_invoice_approver_with_warning() {
572        let approver = WarningPositiveApprover();
573        let amt = 600_000_u64;
574        let invoice = make_test_invoice(1, amt);
575        let success = approver.approve_invoice(&invoice);
576        assert!(success);
577    }
578
579    #[test]
580    fn test_keysend_approver_with_warning() {
581        let clock = Arc::new(ManualClock::new(Duration::ZERO));
582        let approver = WarningPositiveApprover();
583        let (payment_hash, payment_state) = make_keysend_payment(1, clock.now());
584        let success = approver.approve_keysend(payment_hash, payment_state.amount_msat);
585        assert!(success);
586    }
587}