vls_protocol_signer/
approver.rs

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