Merge pull request #1892 from tnull/2022-12-spendableoutputdescriptor-doccs
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Mon, 12 Dec 2022 22:45:00 +0000 (22:45 +0000)
committerGitHub <noreply@github.com>
Mon, 12 Dec 2022 22:45:00 +0000 (22:45 +0000)
Clean up docs in `keysinterface.rs`

16 files changed:
lightning/src/chain/channelmonitor.rs
lightning/src/chain/onchaintx.rs
lightning/src/chain/package.rs
lightning/src/ln/chan_utils.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/features.rs
lightning/src/offers/invoice_request.rs [new file with mode: 0644]
lightning/src/offers/merkle.rs [new file with mode: 0644]
lightning/src/offers/mod.rs
lightning/src/offers/offer.rs
lightning/src/offers/parse.rs
lightning/src/offers/payer.rs [new file with mode: 0644]
lightning/src/util/events.rs
lightning/src/util/ser.rs
lightning/src/util/ser_macros.rs
pending_changelog/matt-abandon-restart.txt [new file with mode: 0644]

index 5c1e102aefc8e2cc44350acc052f99ffdab5e224..a41d853311ce924f388eaa5efbbb2ceb79489a64 100644 (file)
@@ -3032,10 +3032,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
                                                if let Some(new_outputs) = new_outputs_option {
                                                        watch_outputs.push(new_outputs);
                                                }
-                                               // Since there may be multiple HTLCs (all from the same commitment) being
-                                               // claimed by the counterparty within the same transaction, and
-                                               // `check_spend_counterparty_htlc` already checks for all of them, we can
-                                               // safely break from our loop.
+                                               // Since there may be multiple HTLCs for this channel (all spending the
+                                               // same commitment tx) being claimed by the counterparty within the same
+                                               // transaction, and `check_spend_counterparty_htlc` already checks all the
+                                               // ones relevant to this channel, we can safely break from our loop.
                                                break;
                                        }
                                }
index 5bf3a8fd4823369902fba99752ef4b546aebaad3..9bfe6fa9af21b1547e5aa8bf2183062cde66f4cd 100644 (file)
@@ -79,7 +79,7 @@ enum OnchainEvent {
        /// Outpoint under claim process by our own tx, once this one get enough confirmations, we remove it from
        /// bump-txn candidate buffer.
        Claim {
-               claim_request: Txid,
+               package_id: PackageID,
        },
        /// Claim tx aggregate multiple claimable outpoints. One of the outpoint may be claimed by a counterparty party tx.
        /// In this case, we need to drop the outpoint and regenerate a new claim tx. By safety, we keep tracking
@@ -123,7 +123,7 @@ impl MaybeReadable for OnchainEventEntry {
 
 impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
        (0, Claim) => {
-               (0, claim_request, required),
+               (0, package_id, required),
        },
        (1, ContentiousOutpoint) => {
                (0, package, required),
@@ -480,8 +480,8 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                // We check for outpoint spends within claims individually rather than as a set
                                // since requests can have outpoints split off.
                                if !self.onchain_events_awaiting_threshold_conf.iter()
-                                       .any(|event_entry| if let OnchainEvent::Claim { claim_request } = event_entry.event {
-                                               first_claim_txid_height.0 == claim_request.into_inner()
+                                       .any(|event_entry| if let OnchainEvent::Claim { package_id } = event_entry.event {
+                                               first_claim_txid_height.0 == package_id
                                        } else {
                                                // The onchain event is not a claim, keep seeking until we find one.
                                                false
@@ -733,31 +733,13 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                                // outpoints to know if transaction is the original claim or a bumped one issued
                                                // by us.
                                                let mut are_sets_equal = true;
-                                               if !request.requires_external_funding() || !request.is_malleable() {
-                                                       // If the claim does not require external funds to be allocated through
-                                                       // additional inputs we can simply check the inputs in order as they
-                                                       // cannot change under us.
-                                                       if request.outpoints().len() != tx.input.len() {
+                                               let mut tx_inputs = tx.input.iter().map(|input| &input.previous_output).collect::<Vec<_>>();
+                                               tx_inputs.sort_unstable();
+                                               for request_input in request.outpoints() {
+                                                       if tx_inputs.binary_search(&request_input).is_err() {
                                                                are_sets_equal = false;
-                                                       } else {
-                                                               for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
-                                                                       if **claim_inp != tx_inp.previous_output {
-                                                                               are_sets_equal = false;
-                                                                       }
-                                                               }
-                                                       }
-                                               } else {
-                                                       // Otherwise, we'll do a linear search for each input (we don't expect
-                                                       // large input sets to exist) to ensure the request's input set is fully
-                                                       // spent to be resilient against the external claim reordering inputs.
-                                                       let mut spends_all_inputs = true;
-                                                       for request_input in request.outpoints() {
-                                                               if tx.input.iter().find(|input| input.previous_output == *request_input).is_none() {
-                                                                       spends_all_inputs = false;
-                                                                       break;
-                                                               }
+                                                               break;
                                                        }
-                                                       are_sets_equal = spends_all_inputs;
                                                }
 
                                                macro_rules! clean_claim_request_after_safety_delay {
@@ -766,7 +748,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                                                        txid: tx.txid(),
                                                                        height: conf_height,
                                                                        block_hash: Some(conf_hash),
-                                                                       event: OnchainEvent::Claim { claim_request: Txid::from_inner(first_claim_txid_height.0) }
+                                                                       event: OnchainEvent::Claim { package_id: first_claim_txid_height.0 }
                                                                };
                                                                if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
                                                                        self.onchain_events_awaiting_threshold_conf.push(entry);
@@ -821,13 +803,13 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                for entry in onchain_events_awaiting_threshold_conf {
                        if entry.has_reached_confirmation_threshold(cur_height) {
                                match entry.event {
-                                       OnchainEvent::Claim { claim_request } => {
-                                               let package_id = claim_request.into_inner();
+                                       OnchainEvent::Claim { package_id } => {
                                                // We may remove a whole set of claim outpoints here, as these one may have
                                                // been aggregated in a single tx and claimed so atomically
                                                if let Some(request) = self.pending_claim_requests.remove(&package_id) {
                                                        for outpoint in request.outpoints() {
-                                                               log_debug!(logger, "Removing claim tracking for {} due to maturation of claim tx {}.", outpoint, claim_request);
+                                                               log_debug!(logger, "Removing claim tracking for {} due to maturation of claim package {}.",
+                                                                       outpoint, log_bytes!(package_id));
                                                                self.claimable_outpoints.remove(&outpoint);
                                                                #[cfg(anchors)]
                                                                self.pending_claim_events.remove(&package_id);
@@ -1065,7 +1047,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
 
        #[cfg(anchors)]
        pub(crate) fn generate_external_htlc_claim(
-               &mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
+               &self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
        ) -> Option<ExternalHTLCClaim> {
                let find_htlc = |holder_commitment: &HolderCommitmentTransaction| -> Option<ExternalHTLCClaim> {
                        let trusted_tx = holder_commitment.trust();
index dbf46298888ce20a8225c6a11bd88e1394c2552e..227e5ccd119188204b6f374300b0e5dceb777f46 100644 (file)
@@ -203,11 +203,11 @@ impl CounterpartyOfferedHTLCOutput {
 
 impl_writeable_tlv_based!(CounterpartyOfferedHTLCOutput, {
        (0, per_commitment_point, required),
-       (1, opt_anchors, option),
        (2, counterparty_delayed_payment_base_key, required),
        (4, counterparty_htlc_base_key, required),
        (6, preimage, required),
        (8, htlc, required),
+       (10, opt_anchors, option),
 });
 
 /// A struct to describe a HTLC output on a counterparty commitment transaction.
@@ -241,10 +241,10 @@ impl CounterpartyReceivedHTLCOutput {
 
 impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, {
        (0, per_commitment_point, required),
-       (1, opt_anchors, option),
        (2, counterparty_delayed_payment_base_key, required),
        (4, counterparty_htlc_base_key, required),
        (6, htlc, required),
+       (8, opt_anchors, option),
 });
 
 /// A struct to describe a HTLC output on holder commitment transaction.
@@ -318,7 +318,7 @@ impl HolderFundingOutput {
 
 impl_writeable_tlv_based!(HolderFundingOutput, {
        (0, funding_redeemscript, required),
-       (1, opt_anchors, option),
+       (2, opt_anchors, option),
        (3, funding_amount, option),
 });
 
index 729425d70d5bd705bfe65e345567794c8381e64e..408f1cd7e477ca3eb0e4acde601cde9eb0c908f1 100644 (file)
@@ -739,17 +739,12 @@ pub fn build_htlc_input_witness(
        } else {
                EcdsaSighashType::All
        };
-       let mut remote_sig = remote_sig.serialize_der().to_vec();
-       remote_sig.push(remote_sighash_type as u8);
-
-       let mut local_sig = local_sig.serialize_der().to_vec();
-       local_sig.push(EcdsaSighashType::All as u8);
 
        let mut witness = Witness::new();
        // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
        witness.push(vec![]);
-       witness.push(remote_sig);
-       witness.push(local_sig);
+       witness.push_bitcoin_signature(&remote_sig.serialize_der(), remote_sighash_type);
+       witness.push_bitcoin_signature(&local_sig.serialize_der(), EcdsaSighashType::All);
        if let Some(preimage) = preimage {
                witness.push(preimage.0.to_vec());
        } else {
@@ -801,9 +796,10 @@ pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubk
 /// Returns the witness required to satisfy and spend an anchor input.
 pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness {
        let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key);
-       let mut funding_sig = funding_sig.serialize_der().to_vec();
-       funding_sig.push(EcdsaSighashType::All as u8);
-       Witness::from_vec(vec![funding_sig, anchor_redeem_script.to_bytes()])
+       let mut ret = Witness::new();
+       ret.push_bitcoin_signature(&funding_sig.serialize_der(), EcdsaSighashType::All);
+       ret.push(anchor_redeem_script.as_bytes());
+       ret
 }
 
 /// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction).
@@ -1037,17 +1033,13 @@ impl HolderCommitmentTransaction {
                // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
                let mut tx = self.inner.built.transaction.clone();
                tx.input[0].witness.push(Vec::new());
-               let mut ser_holder_sig = holder_sig.serialize_der().to_vec();
-               ser_holder_sig.push(EcdsaSighashType::All as u8);
-               let mut ser_cp_sig = self.counterparty_sig.serialize_der().to_vec();
-               ser_cp_sig.push(EcdsaSighashType::All as u8);
 
                if self.holder_sig_first {
-                       tx.input[0].witness.push(ser_holder_sig);
-                       tx.input[0].witness.push(ser_cp_sig);
+                       tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
+                       tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
                } else {
-                       tx.input[0].witness.push(ser_cp_sig);
-                       tx.input[0].witness.push(ser_holder_sig);
+                       tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
+                       tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
                }
 
                tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
index bb99b02d9f9a90bdafee770671100a22a95e2e70..16c5ec68a7debd1dbd7c262883e5109f534ac899 100644 (file)
@@ -283,14 +283,6 @@ struct ReceiveError {
        msg: &'static str,
 }
 
-/// Return value for claim_funds_from_hop
-enum ClaimFundsFromHop {
-       PrevHopForceClosed,
-       MonitorUpdateFail(PublicKey, MsgHandleErrInternal, Option<u64>),
-       Success(u64),
-       DuplicateClaim,
-}
-
 type ShutdownResult = (Option<(OutPoint, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash, PublicKey, [u8; 32])>);
 
 /// Error type returned across the channel_state mutex boundary. When an Err is generated for a
@@ -404,6 +396,36 @@ pub(super) enum RAACommitmentOrder {
        RevokeAndACKFirst,
 }
 
+/// Information about a payment which is currently being claimed.
+struct ClaimingPayment {
+       amount_msat: u64,
+       payment_purpose: events::PaymentPurpose,
+       receiver_node_id: PublicKey,
+}
+impl_writeable_tlv_based!(ClaimingPayment, {
+       (0, amount_msat, required),
+       (2, payment_purpose, required),
+       (4, receiver_node_id, required),
+});
+
+/// Information about claimable or being-claimed payments
+struct ClaimablePayments {
+       /// Map from payment hash to the payment data and any HTLCs which are to us and can be
+       /// failed/claimed by the user.
+       ///
+       /// Note that, no consistency guarantees are made about the channels given here actually
+       /// existing anymore by the time you go to read them!
+       ///
+       /// When adding to the map, [`Self::pending_claiming_payments`] must also be checked to ensure
+       /// we don't get a duplicate payment.
+       claimable_htlcs: HashMap<PaymentHash, (events::PaymentPurpose, Vec<ClaimableHTLC>)>,
+
+       /// Map from payment hash to the payment data for HTLCs which we have begun claiming, but which
+       /// are waiting on a [`ChannelMonitorUpdate`] to complete in order to be surfaced to the user
+       /// as an [`events::Event::PaymentClaimed`].
+       pending_claiming_payments: HashMap<PaymentHash, ClaimingPayment>,
+}
+
 // Note this is only exposed in cfg(test):
 pub(super) struct ChannelHolder<Signer: Sign> {
        pub(super) by_id: HashMap<[u8; 32], Channel<Signer>>,
@@ -421,6 +443,16 @@ enum BackgroundEvent {
        ClosingMonitorUpdate((OutPoint, ChannelMonitorUpdate)),
 }
 
+pub(crate) enum MonitorUpdateCompletionAction {
+       /// Indicates that a payment ultimately destined for us was claimed and we should emit an
+       /// [`events::Event::PaymentClaimed`] to the user if we haven't yet generated such an event for
+       /// this payment. Note that this is only best-effort. On restart it's possible such a duplicate
+       /// event can be generated.
+       PaymentClaimed { payment_hash: PaymentHash },
+       /// Indicates an [`events::Event`] should be surfaced to the user.
+       EmitEvent { event: events::Event },
+}
+
 /// State we hold per-peer. In the future we should put channels in here, but for now we only hold
 /// the latest Init features we heard from the peer.
 struct PeerState {
@@ -679,7 +711,7 @@ pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManage
 //  |
 //  |__`pending_inbound_payments`
 //  |   |
-//  |   |__`claimable_htlcs`
+//  |   |__`claimable_payments`
 //  |   |
 //  |   |__`pending_outbound_payments`
 //  |       |
@@ -767,14 +799,11 @@ pub struct ChannelManager<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
        /// See `ChannelManager` struct-level documentation for lock order requirements.
        pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
 
-       /// Map from payment hash to the payment data and any HTLCs which are to us and can be
-       /// failed/claimed by the user.
-       ///
-       /// Note that, no consistency guarantees are made about the channels given here actually
-       /// existing anymore by the time you go to read them!
+       /// The sets of payments which are claimable or currently being claimed. See
+       /// [`ClaimablePayments`]' individual field docs for more info.
        ///
        /// See `ChannelManager` struct-level documentation for lock order requirements.
-       claimable_htlcs: Mutex<HashMap<PaymentHash, (events::PaymentPurpose, Vec<ClaimableHTLC>)>>,
+       claimable_payments: Mutex<ClaimablePayments>,
 
        /// The set of outbound SCID aliases across all our channels, including unconfirmed channels
        /// and some closed channels which reached a usable state prior to being closed. This is used
@@ -1580,7 +1609,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                        pending_inbound_payments: Mutex::new(HashMap::new()),
                        pending_outbound_payments: Mutex::new(HashMap::new()),
                        forward_htlcs: Mutex::new(HashMap::new()),
-                       claimable_htlcs: Mutex::new(HashMap::new()),
+                       claimable_payments: Mutex::new(ClaimablePayments { claimable_htlcs: HashMap::new(), pending_claiming_payments: HashMap::new() }),
                        pending_intercepted_htlcs: Mutex::new(HashMap::new()),
                        id_to_peer: Mutex::new(HashMap::new()),
                        short_to_chan_info: FairRwLock::new(HashMap::new()),
@@ -2761,15 +2790,21 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
 
        /// Signals that no further retries for the given payment will occur.
        ///
-       /// After this method returns, any future calls to [`retry_payment`] for the given `payment_id`
-       /// will fail with [`PaymentSendFailure::ParameterError`]. If no such event has been generated,
-       /// an [`Event::PaymentFailed`] event will be generated as soon as there are no remaining
-       /// pending HTLCs for this payment.
+       /// After this method returns, no future calls to [`retry_payment`] for the given `payment_id`
+       /// are allowed. If no [`Event::PaymentFailed`] event had been generated before, one will be
+       /// generated as soon as there are no remaining pending HTLCs for this payment.
        ///
        /// Note that calling this method does *not* prevent a payment from succeeding. You must still
        /// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
        /// determine the ultimate status of a payment.
        ///
+       /// If an [`Event::PaymentFailed`] event is generated and we restart without this
+       /// [`ChannelManager`] having been persisted, the payment may still be in the pending state
+       /// upon restart. This allows further calls to [`retry_payment`] (and requiring a second call
+       /// to [`abandon_payment`] to mark the payment as failed again). Otherwise, future calls to
+       /// [`retry_payment`] will fail with [`PaymentSendFailure::ParameterError`].
+       ///
+       /// [`abandon_payment`]: Self::abandon_payment
        /// [`retry_payment`]: Self::retry_payment
        /// [`Event::PaymentFailed`]: events::Event::PaymentFailed
        /// [`Event::PaymentSent`]: events::Event::PaymentSent
@@ -3405,8 +3440,12 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                                                payment_secret: $payment_data.payment_secret,
                                                                                        }
                                                                                };
-                                                                               let mut claimable_htlcs = self.claimable_htlcs.lock().unwrap();
-                                                                               let (_, htlcs) = claimable_htlcs.entry(payment_hash)
+                                                                               let mut claimable_payments = self.claimable_payments.lock().unwrap();
+                                                                               if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
+                                                                                       fail_htlc!(claimable_htlc, payment_hash);
+                                                                                       continue
+                                                                               }
+                                                                               let (_, htlcs) = claimable_payments.claimable_htlcs.entry(payment_hash)
                                                                                        .or_insert_with(|| (purpose(), Vec::new()));
                                                                                if htlcs.len() == 1 {
                                                                                        if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
@@ -3478,7 +3517,12 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                                                check_total_value!(payment_data, payment_preimage);
                                                                                        },
                                                                                        OnionPayload::Spontaneous(preimage) => {
-                                                                                               match self.claimable_htlcs.lock().unwrap().entry(payment_hash) {
+                                                                                               let mut claimable_payments = self.claimable_payments.lock().unwrap();
+                                                                                               if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
+                                                                                                       fail_htlc!(claimable_htlc, payment_hash);
+                                                                                                       continue
+                                                                                               }
+                                                                                               match claimable_payments.claimable_htlcs.entry(payment_hash) {
                                                                                                        hash_map::Entry::Vacant(e) => {
                                                                                                                let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
                                                                                                                e.insert((purpose.clone(), vec![claimable_htlc]));
@@ -3726,7 +3770,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                });
                        }
 
-                       self.claimable_htlcs.lock().unwrap().retain(|payment_hash, (_, htlcs)| {
+                       self.claimable_payments.lock().unwrap().claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
                                if htlcs.is_empty() {
                                        // This should be unreachable
                                        debug_assert!(false);
@@ -3788,7 +3832,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
        pub fn fail_htlc_backwards(&self, payment_hash: &PaymentHash) {
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
 
-               let removed_source = self.claimable_htlcs.lock().unwrap().remove(payment_hash);
+               let removed_source = self.claimable_payments.lock().unwrap().claimable_htlcs.remove(payment_hash);
                if let Some((_, mut sources)) = removed_source {
                        for htlc in sources.drain(..) {
                                let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
@@ -4027,141 +4071,150 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
        /// [`process_pending_events`]: EventsProvider::process_pending_events
        /// [`create_inbound_payment`]: Self::create_inbound_payment
        /// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
-       /// [`get_and_clear_pending_msg_events`]: MessageSendEventsProvider::get_and_clear_pending_msg_events
        pub fn claim_funds(&self, payment_preimage: PaymentPreimage) {
                let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
 
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
 
-               let removed_source = self.claimable_htlcs.lock().unwrap().remove(&payment_hash);
-               if let Some((payment_purpose, mut sources)) = removed_source {
-                       assert!(!sources.is_empty());
-
-                       // If we are claiming an MPP payment, we have to take special care to ensure that each
-                       // channel exists before claiming all of the payments (inside one lock).
-                       // Note that channel existance is sufficient as we should always get a monitor update
-                       // which will take care of the real HTLC claim enforcement.
-                       //
-                       // If we find an HTLC which we would need to claim but for which we do not have a
-                       // channel, we will fail all parts of the MPP payment. While we could wait and see if
-                       // the sender retries the already-failed path(s), it should be a pretty rare case where
-                       // we got all the HTLCs and then a channel closed while we were waiting for the user to
-                       // provide the preimage, so worrying too much about the optimal handling isn't worth
-                       // it.
-                       let mut claimable_amt_msat = 0;
-                       let mut expected_amt_msat = None;
-                       let mut valid_mpp = true;
-                       let mut errs = Vec::new();
-                       let mut claimed_any_htlcs = false;
-                       let mut channel_state_lock = self.channel_state.lock().unwrap();
-                       let channel_state = &mut *channel_state_lock;
-                       let mut receiver_node_id = Some(self.our_network_pubkey);
-                       for htlc in sources.iter() {
-                               let chan_id = match self.short_to_chan_info.read().unwrap().get(&htlc.prev_hop.short_channel_id) {
-                                       Some((_cp_id, chan_id)) => chan_id.clone(),
-                                       None => {
-                                               valid_mpp = false;
+               let mut sources = {
+                       let mut claimable_payments = self.claimable_payments.lock().unwrap();
+                       if let Some((payment_purpose, sources)) = claimable_payments.claimable_htlcs.remove(&payment_hash) {
+                               let mut receiver_node_id = self.our_network_pubkey;
+                               for htlc in sources.iter() {
+                                       if htlc.prev_hop.phantom_shared_secret.is_some() {
+                                               let phantom_pubkey = self.keys_manager.get_node_id(Recipient::PhantomNode)
+                                                       .expect("Failed to get node_id for phantom node recipient");
+                                               receiver_node_id = phantom_pubkey;
                                                break;
                                        }
-                               };
+                               }
 
-                               if let None = channel_state.by_id.get(&chan_id) {
-                                       valid_mpp = false;
-                                       break;
+                               let dup_purpose = claimable_payments.pending_claiming_payments.insert(payment_hash,
+                                       ClaimingPayment { amount_msat: sources.iter().map(|source| source.value).sum(),
+                                       payment_purpose, receiver_node_id,
+                               });
+                               if dup_purpose.is_some() {
+                                       debug_assert!(false, "Shouldn't get a duplicate pending claim event ever");
+                                       log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
+                                               log_bytes!(payment_hash.0));
                                }
+                               sources
+                       } else { return; }
+               };
+               debug_assert!(!sources.is_empty());
 
-                               if expected_amt_msat.is_some() && expected_amt_msat != Some(htlc.total_msat) {
-                                       log_error!(self.logger, "Somehow ended up with an MPP payment with different total amounts - this should not be reachable!");
-                                       debug_assert!(false);
+               // If we are claiming an MPP payment, we check that all channels which contain a claimable
+               // HTLC still exist. While this isn't guaranteed to remain true if a channel closes while
+               // we're claiming (or even after we claim, before the commitment update dance completes),
+               // it should be a relatively rare race, and we'd rather not claim HTLCs that require us to
+               // go on-chain (and lose the on-chain fee to do so) than just reject the payment.
+               //
+               // Note that we'll still always get our funds - as long as the generated
+               // `ChannelMonitorUpdate` makes it out to the relevant monitor we can claim on-chain.
+               //
+               // If we find an HTLC which we would need to claim but for which we do not have a
+               // channel, we will fail all parts of the MPP payment. While we could wait and see if
+               // the sender retries the already-failed path(s), it should be a pretty rare case where
+               // we got all the HTLCs and then a channel closed while we were waiting for the user to
+               // provide the preimage, so worrying too much about the optimal handling isn't worth
+               // it.
+               let mut claimable_amt_msat = 0;
+               let mut expected_amt_msat = None;
+               let mut valid_mpp = true;
+               let mut errs = Vec::new();
+               let mut channel_state = Some(self.channel_state.lock().unwrap());
+               for htlc in sources.iter() {
+                       let chan_id = match self.short_to_chan_info.read().unwrap().get(&htlc.prev_hop.short_channel_id) {
+                               Some((_cp_id, chan_id)) => chan_id.clone(),
+                               None => {
                                        valid_mpp = false;
                                        break;
                                }
-                               expected_amt_msat = Some(htlc.total_msat);
-                               if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
-                                       // We don't currently support MPP for spontaneous payments, so just check
-                                       // that there's one payment here and move on.
-                                       if sources.len() != 1 {
-                                               log_error!(self.logger, "Somehow ended up with an MPP spontaneous payment - this should not be reachable!");
-                                               debug_assert!(false);
-                                               valid_mpp = false;
-                                               break;
-                                       }
-                               }
-                               let phantom_shared_secret = htlc.prev_hop.phantom_shared_secret;
-                               if phantom_shared_secret.is_some() {
-                                       let phantom_pubkey = self.keys_manager.get_node_id(Recipient::PhantomNode)
-                                               .expect("Failed to get node_id for phantom node recipient");
-                                       receiver_node_id = Some(phantom_pubkey)
-                               }
+                       };
 
-                               claimable_amt_msat += htlc.value;
-                       }
-                       if sources.is_empty() || expected_amt_msat.is_none() {
-                               log_info!(self.logger, "Attempted to claim an incomplete payment which no longer had any available HTLCs!");
-                               return;
+                       if let None = channel_state.as_ref().unwrap().by_id.get(&chan_id) {
+                               valid_mpp = false;
+                               break;
                        }
-                       if claimable_amt_msat != expected_amt_msat.unwrap() {
-                               log_info!(self.logger, "Attempted to claim an incomplete payment, expected {} msat, had {} available to claim.",
-                                       expected_amt_msat.unwrap(), claimable_amt_msat);
-                               return;
+
+                       if expected_amt_msat.is_some() && expected_amt_msat != Some(htlc.total_msat) {
+                               log_error!(self.logger, "Somehow ended up with an MPP payment with different total amounts - this should not be reachable!");
+                               debug_assert!(false);
+                               valid_mpp = false;
+                               break;
                        }
-                       if valid_mpp {
-                               for htlc in sources.drain(..) {
-                                       match self.claim_funds_from_hop(&mut channel_state_lock, htlc.prev_hop, payment_preimage) {
-                                               ClaimFundsFromHop::MonitorUpdateFail(pk, err, _) => {
-                                                       if let msgs::ErrorAction::IgnoreError = err.err.action {
-                                                               // We got a temporary failure updating monitor, but will claim the
-                                                               // HTLC when the monitor updating is restored (or on chain).
-                                                               log_error!(self.logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
-                                                               claimed_any_htlcs = true;
-                                                       } else { errs.push((pk, err)); }
-                                               },
-                                               ClaimFundsFromHop::PrevHopForceClosed => unreachable!("We already checked for channel existence, we can't fail here!"),
-                                               ClaimFundsFromHop::DuplicateClaim => {
-                                                       // While we should never get here in most cases, if we do, it likely
-                                                       // indicates that the HTLC was timed out some time ago and is no longer
-                                                       // available to be claimed. Thus, it does not make sense to set
-                                                       // `claimed_any_htlcs`.
-                                               },
-                                               ClaimFundsFromHop::Success(_) => claimed_any_htlcs = true,
-                                       }
+                       expected_amt_msat = Some(htlc.total_msat);
+                       if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
+                               // We don't currently support MPP for spontaneous payments, so just check
+                               // that there's one payment here and move on.
+                               if sources.len() != 1 {
+                                       log_error!(self.logger, "Somehow ended up with an MPP spontaneous payment - this should not be reachable!");
+                                       debug_assert!(false);
+                                       valid_mpp = false;
+                                       break;
                                }
                        }
-                       mem::drop(channel_state_lock);
-                       if !valid_mpp {
-                               for htlc in sources.drain(..) {
-                                       let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
-                                       htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
-                                       let source = HTLCSource::PreviousHopData(htlc.prev_hop);
-                                       let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
-                                       let receiver = HTLCDestination::FailedPayment { payment_hash };
-                                       self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
+
+                       claimable_amt_msat += htlc.value;
+               }
+               if sources.is_empty() || expected_amt_msat.is_none() {
+                       mem::drop(channel_state);
+                       self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+                       log_info!(self.logger, "Attempted to claim an incomplete payment which no longer had any available HTLCs!");
+                       return;
+               }
+               if claimable_amt_msat != expected_amt_msat.unwrap() {
+                       mem::drop(channel_state);
+                       self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+                       log_info!(self.logger, "Attempted to claim an incomplete payment, expected {} msat, had {} available to claim.",
+                               expected_amt_msat.unwrap(), claimable_amt_msat);
+                       return;
+               }
+               if valid_mpp {
+                       for htlc in sources.drain(..) {
+                               if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
+                               if let Err((pk, err)) = self.claim_funds_from_hop(channel_state.take().unwrap(), htlc.prev_hop,
+                                       payment_preimage,
+                                       |_| Some(MonitorUpdateCompletionAction::PaymentClaimed { payment_hash }))
+                               {
+                                       if let msgs::ErrorAction::IgnoreError = err.err.action {
+                                               // We got a temporary failure updating monitor, but will claim the
+                                               // HTLC when the monitor updating is restored (or on chain).
+                                               log_error!(self.logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
+                                       } else { errs.push((pk, err)); }
                                }
                        }
-
-                       if claimed_any_htlcs {
-                               self.pending_events.lock().unwrap().push(events::Event::PaymentClaimed {
-                                       receiver_node_id,
-                                       payment_hash,
-                                       purpose: payment_purpose,
-                                       amount_msat: claimable_amt_msat,
-                               });
+               }
+               mem::drop(channel_state);
+               if !valid_mpp {
+                       for htlc in sources.drain(..) {
+                               let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
+                               htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
+                               let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+                               let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+                               let receiver = HTLCDestination::FailedPayment { payment_hash };
+                               self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                        }
+                       self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+               }
 
-                       // Now we can handle any errors which were generated.
-                       for (counterparty_node_id, err) in errs.drain(..) {
-                               let res: Result<(), _> = Err(err);
-                               let _ = handle_error!(self, res, counterparty_node_id);
-                       }
+               // Now we can handle any errors which were generated.
+               for (counterparty_node_id, err) in errs.drain(..) {
+                       let res: Result<(), _> = Err(err);
+                       let _ = handle_error!(self, res, counterparty_node_id);
                }
        }
 
-       fn claim_funds_from_hop(&self, channel_state_lock: &mut MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage) -> ClaimFundsFromHop {
+       fn claim_funds_from_hop<ComplFunc: FnOnce(Option<u64>) -> Option<MonitorUpdateCompletionAction>>(&self,
+               mut channel_state_lock: MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>,
+               prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage, completion_action: ComplFunc)
+       -> Result<(), (PublicKey, MsgHandleErrInternal)> {
                //TODO: Delay the claimed_funds relaying just like we do outbound relay!
 
                let chan_id = prev_hop.outpoint.to_channel_id();
-               let channel_state = &mut **channel_state_lock;
+               let channel_state = &mut *channel_state_lock;
                if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(chan_id) {
+                       let counterparty_node_id = chan.get().get_counterparty_node_id();
                        match chan.get_mut().get_update_fulfill_htlc_and_commit(prev_hop.htlc_id, payment_preimage, &self.logger) {
                                Ok(msgs_monitor_option) => {
                                        if let UpdateFulfillCommitFetch::NewClaim { msgs, htlc_value_msat, monitor_update } = msgs_monitor_option {
@@ -4171,11 +4224,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                log_given_level!(self.logger, if e == ChannelMonitorUpdateStatus::PermanentFailure { Level::Error } else { Level::Debug },
                                                                        "Failed to update channel monitor with preimage {:?}: {:?}",
                                                                        payment_preimage, e);
-                                                               return ClaimFundsFromHop::MonitorUpdateFail(
-                                                                       chan.get().get_counterparty_node_id(),
-                                                                       handle_monitor_update_res!(self, e, chan, RAACommitmentOrder::CommitmentFirst, false, msgs.is_some()).unwrap_err(),
-                                                                       Some(htlc_value_msat)
-                                                               );
+                                                               let err = handle_monitor_update_res!(self, e, chan, RAACommitmentOrder::CommitmentFirst, false, msgs.is_some()).unwrap_err();
+                                                               mem::drop(channel_state_lock);
+                                                               self.handle_monitor_update_completion_actions(completion_action(Some(htlc_value_msat)));
+                                                               return Err((counterparty_node_id, err));
                                                        }
                                                }
                                                if let Some((msg, commitment_signed)) = msgs {
@@ -4193,29 +4245,62 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                }
                                                        });
                                                }
-                                               return ClaimFundsFromHop::Success(htlc_value_msat);
+                                               mem::drop(channel_state_lock);
+                                               self.handle_monitor_update_completion_actions(completion_action(Some(htlc_value_msat)));
+                                               Ok(())
                                        } else {
-                                               return ClaimFundsFromHop::DuplicateClaim;
+                                               Ok(())
                                        }
                                },
                                Err((e, monitor_update)) => {
                                        match self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), monitor_update) {
                                                ChannelMonitorUpdateStatus::Completed => {},
                                                e => {
+                                                       // TODO: This needs to be handled somehow - if we receive a monitor update
+                                                       // with a preimage we *must* somehow manage to propagate it to the upstream
+                                                       // channel, or we must have an ability to receive the same update and try
+                                                       // again on restart.
                                                        log_given_level!(self.logger, if e == ChannelMonitorUpdateStatus::PermanentFailure { Level::Error } else { Level::Info },
                                                                "Failed to update channel monitor with preimage {:?} immediately prior to force-close: {:?}",
                                                                payment_preimage, e);
                                                },
                                        }
-                                       let counterparty_node_id = chan.get().get_counterparty_node_id();
                                        let (drop, res) = convert_chan_err!(self, e, chan.get_mut(), &chan_id);
                                        if drop {
                                                chan.remove_entry();
                                        }
-                                       return ClaimFundsFromHop::MonitorUpdateFail(counterparty_node_id, res, None);
+                                       mem::drop(channel_state_lock);
+                                       self.handle_monitor_update_completion_actions(completion_action(None));
+                                       Err((counterparty_node_id, res))
                                },
                        }
-               } else { return ClaimFundsFromHop::PrevHopForceClosed }
+               } else {
+                       let preimage_update = ChannelMonitorUpdate {
+                               update_id: CLOSED_CHANNEL_UPDATE_ID,
+                               updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
+                                       payment_preimage,
+                               }],
+                       };
+                       // We update the ChannelMonitor on the backward link, after
+                       // receiving an `update_fulfill_htlc` from the forward link.
+                       let update_res = self.chain_monitor.update_channel(prev_hop.outpoint, preimage_update);
+                       if update_res != ChannelMonitorUpdateStatus::Completed {
+                               // TODO: This needs to be handled somehow - if we receive a monitor update
+                               // with a preimage we *must* somehow manage to propagate it to the upstream
+                               // channel, or we must have an ability to receive the same event and try
+                               // again on restart.
+                               log_error!(self.logger, "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
+                                       payment_preimage, update_res);
+                       }
+                       mem::drop(channel_state_lock);
+                       // Note that we do process the completion action here. This totally could be a
+                       // duplicate claim, but we have no way of knowing without interrogating the
+                       // `ChannelMonitor` we've provided the above update to. Instead, note that `Event`s are
+                       // generally always allowed to be duplicative (and it's specifically noted in
+                       // `PaymentForwarded`).
+                       self.handle_monitor_update_completion_actions(completion_action(None));
+                       Ok(())
+               }
        }
 
        fn finalize_claims(&self, mut sources: Vec<HTLCSource>) {
@@ -4241,7 +4326,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                }
        }
 
-       fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool, next_channel_id: [u8; 32]) {
+       fn claim_funds_internal(&self, channel_state_lock: MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool, next_channel_id: [u8; 32]) {
                match source {
                        HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
                                mem::drop(channel_state_lock);
@@ -4288,62 +4373,28 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                        },
                        HTLCSource::PreviousHopData(hop_data) => {
                                let prev_outpoint = hop_data.outpoint;
-                               let res = self.claim_funds_from_hop(&mut channel_state_lock, hop_data, payment_preimage);
-                               let claimed_htlc = if let ClaimFundsFromHop::DuplicateClaim = res { false } else { true };
-                               let htlc_claim_value_msat = match res {
-                                       ClaimFundsFromHop::MonitorUpdateFail(_, _, amt_opt) => amt_opt,
-                                       ClaimFundsFromHop::Success(amt) => Some(amt),
-                                       _ => None,
-                               };
-                               if let ClaimFundsFromHop::PrevHopForceClosed = res {
-                                       let preimage_update = ChannelMonitorUpdate {
-                                               update_id: CLOSED_CHANNEL_UPDATE_ID,
-                                               updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
-                                                       payment_preimage: payment_preimage.clone(),
-                                               }],
-                                       };
-                                       // We update the ChannelMonitor on the backward link, after
-                                       // receiving an offchain preimage event from the forward link (the
-                                       // event being update_fulfill_htlc).
-                                       let update_res = self.chain_monitor.update_channel(prev_outpoint, preimage_update);
-                                       if update_res != ChannelMonitorUpdateStatus::Completed {
-                                               // TODO: This needs to be handled somehow - if we receive a monitor update
-                                               // with a preimage we *must* somehow manage to propagate it to the upstream
-                                               // channel, or we must have an ability to receive the same event and try
-                                               // again on restart.
-                                               log_error!(self.logger, "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
-                                                       payment_preimage, update_res);
-                                       }
-                                       // Note that we do *not* set `claimed_htlc` to false here. In fact, this
-                                       // totally could be a duplicate claim, but we have no way of knowing
-                                       // without interrogating the `ChannelMonitor` we've provided the above
-                                       // update to. Instead, we simply document in `PaymentForwarded` that this
-                                       // can happen.
-                               }
-                               mem::drop(channel_state_lock);
-                               if let ClaimFundsFromHop::MonitorUpdateFail(pk, err, _) = res {
+                               let res = self.claim_funds_from_hop(channel_state_lock, hop_data, payment_preimage,
+                                       |htlc_claim_value_msat| {
+                                               if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
+                                                       let fee_earned_msat = if let Some(claimed_htlc_value) = htlc_claim_value_msat {
+                                                               Some(claimed_htlc_value - forwarded_htlc_value)
+                                                       } else { None };
+
+                                                       let prev_channel_id = Some(prev_outpoint.to_channel_id());
+                                                       let next_channel_id = Some(next_channel_id);
+
+                                                       Some(MonitorUpdateCompletionAction::EmitEvent { event: events::Event::PaymentForwarded {
+                                                               fee_earned_msat,
+                                                               claim_from_onchain_tx: from_onchain,
+                                                               prev_channel_id,
+                                                               next_channel_id,
+                                                       }})
+                                               } else { None }
+                                       });
+                               if let Err((pk, err)) = res {
                                        let result: Result<(), _> = Err(err);
                                        let _ = handle_error!(self, result, pk);
                                }
-
-                               if claimed_htlc {
-                                       if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
-                                               let fee_earned_msat = if let Some(claimed_htlc_value) = htlc_claim_value_msat {
-                                                       Some(claimed_htlc_value - forwarded_htlc_value)
-                                               } else { None };
-
-                                               let mut pending_events = self.pending_events.lock().unwrap();
-                                               let prev_channel_id = Some(prev_outpoint.to_channel_id());
-                                               let next_channel_id = Some(next_channel_id);
-
-                                               pending_events.push(events::Event::PaymentForwarded {
-                                                       fee_earned_msat,
-                                                       claim_from_onchain_tx: from_onchain,
-                                                       prev_channel_id,
-                                                       next_channel_id,
-                                               });
-                                       }
-                               }
                        },
                }
        }
@@ -4353,6 +4404,24 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                self.our_network_pubkey.clone()
        }
 
+       fn handle_monitor_update_completion_actions<I: IntoIterator<Item=MonitorUpdateCompletionAction>>(&self, actions: I) {
+               for action in actions.into_iter() {
+                       match action {
+                               MonitorUpdateCompletionAction::PaymentClaimed { payment_hash } => {
+                                       let payment = self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+                                       if let Some(ClaimingPayment { amount_msat, payment_purpose: purpose, receiver_node_id }) = payment {
+                                               self.pending_events.lock().unwrap().push(events::Event::PaymentClaimed {
+                                                       payment_hash, purpose, amount_msat, receiver_node_id: Some(receiver_node_id),
+                                               });
+                                       }
+                               },
+                               MonitorUpdateCompletionAction::EmitEvent { event } => {
+                                       self.pending_events.lock().unwrap().push(event);
+                               },
+                       }
+               }
+       }
+
        /// Handles a channel reentering a functional state, either due to reconnect or a monitor
        /// update completion.
        fn handle_channel_resumption(&self, pending_msg_events: &mut Vec<MessageSendEvent>,
@@ -6100,7 +6169,7 @@ where
                }
 
                if let Some(height) = height_opt {
-                       self.claimable_htlcs.lock().unwrap().retain(|payment_hash, (_, htlcs)| {
+                       self.claimable_payments.lock().unwrap().claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
                                htlcs.retain(|htlc| {
                                        // If height is approaching the number of blocks we think it takes us to get
                                        // our commitment transaction confirmed before the HTLC expires, plus the
@@ -6955,12 +7024,12 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelMana
                }
 
                let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
-               let claimable_htlcs = self.claimable_htlcs.lock().unwrap();
+               let claimable_payments = self.claimable_payments.lock().unwrap();
                let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
 
                let mut htlc_purposes: Vec<&events::PaymentPurpose> = Vec::new();
-               (claimable_htlcs.len() as u64).write(writer)?;
-               for (payment_hash, (purpose, previous_hops)) in claimable_htlcs.iter() {
+               (claimable_payments.claimable_htlcs.len() as u64).write(writer)?;
+               for (payment_hash, (purpose, previous_hops)) in claimable_payments.claimable_htlcs.iter() {
                        payment_hash.write(writer)?;
                        (previous_hops.len() as u64).write(writer)?;
                        for htlc in previous_hops.iter() {
@@ -7045,10 +7114,21 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelMana
                if our_pending_intercepts.len() != 0 {
                        pending_intercepted_htlcs = Some(our_pending_intercepts);
                }
+
+               let mut pending_claiming_payments = Some(&claimable_payments.pending_claiming_payments);
+               if pending_claiming_payments.as_ref().unwrap().is_empty() {
+                       // LDK versions prior to 0.0.113 do not know how to read the pending claimed payments
+                       // map. Thus, if there are no entries we skip writing a TLV for it.
+                       pending_claiming_payments = None;
+               } else {
+                       debug_assert!(false, "While we have code to serialize pending_claiming_payments, the map should always be empty until a later PR");
+               }
+
                write_tlv_fields!(writer, {
                        (1, pending_outbound_payments_no_retry, required),
                        (2, pending_intercepted_htlcs, option),
                        (3, pending_outbound_payments, required),
+                       (4, pending_claiming_payments, option),
                        (5, self.our_network_pubkey, required),
                        (7, self.fake_scid_rand_bytes, required),
                        (9, htlc_purposes, vec_type),
@@ -7375,10 +7455,12 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
                let mut probing_cookie_secret: Option<[u8; 32]> = None;
                let mut claimable_htlc_purposes = None;
+               let mut pending_claiming_payments = Some(HashMap::new());
                read_tlv_fields!(reader, {
                        (1, pending_outbound_payments_no_retry, option),
                        (2, pending_intercepted_htlcs, option),
                        (3, pending_outbound_payments, option),
+                       (4, pending_claiming_payments, option),
                        (5, received_network_pubkey, option),
                        (7, fake_scid_rand_bytes, option),
                        (9, claimable_htlc_purposes, vec_type),
@@ -7637,7 +7719,7 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                        pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
 
                        forward_htlcs: Mutex::new(forward_htlcs),
-                       claimable_htlcs: Mutex::new(claimable_htlcs),
+                       claimable_payments: Mutex::new(ClaimablePayments { claimable_htlcs, pending_claiming_payments: pending_claiming_payments.unwrap() }),
                        outbound_scid_aliases: Mutex::new(outbound_scid_aliases),
                        id_to_peer: Mutex::new(id_to_peer),
                        short_to_chan_info: FairRwLock::new(short_to_chan_info),
index 77d0fa4529fb2ea526682079229b81fa9165ddf6..1f455471a9f4fa2bf3b30b9a1ade5ac54638bb72 100644 (file)
@@ -158,6 +158,7 @@ mod sealed {
                BasicMPP,
        ]);
        define_context!(OfferContext, []);
+       define_context!(InvoiceRequestContext, []);
        // This isn't a "real" feature context, and is only used in the channel_type field in an
        // `OpenChannel` message.
        define_context!(ChannelTypeContext, [
@@ -367,7 +368,8 @@ mod sealed {
                supports_keysend, requires_keysend);
 
        #[cfg(test)]
-       define_feature!(123456789, UnknownFeature, [NodeContext, ChannelContext, InvoiceContext, OfferContext],
+       define_feature!(123456789, UnknownFeature,
+               [NodeContext, ChannelContext, InvoiceContext, OfferContext, InvoiceRequestContext],
                "Feature flags for an unknown feature used in testing.", set_unknown_feature_optional,
                set_unknown_feature_required, supports_unknown_test_feature, requires_unknown_test_feature);
 }
@@ -426,8 +428,10 @@ pub type NodeFeatures = Features<sealed::NodeContext>;
 pub type ChannelFeatures = Features<sealed::ChannelContext>;
 /// Features used within an invoice.
 pub type InvoiceFeatures = Features<sealed::InvoiceContext>;
-/// Features used within an offer.
+/// Features used within an `offer`.
 pub type OfferFeatures = Features<sealed::OfferContext>;
+/// Features used within an `invoice_request`.
+pub type InvoiceRequestFeatures = Features<sealed::InvoiceRequestContext>;
 
 /// Features used within the channel_type field in an OpenChannel message.
 ///
@@ -735,6 +739,7 @@ macro_rules! impl_feature_tlv_write {
 
 impl_feature_tlv_write!(ChannelTypeFeatures);
 impl_feature_tlv_write!(OfferFeatures);
+impl_feature_tlv_write!(InvoiceRequestFeatures);
 
 #[cfg(test)]
 mod tests {
diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs
new file mode 100644 (file)
index 0000000..90f6c18
--- /dev/null
@@ -0,0 +1,1276 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Data structures and encoding for `invoice_request` messages.
+//!
+//! An [`InvoiceRequest`] can be either built from a parsed [`Offer`] as an "offer to be paid" or
+//! built directly as an "offer for money" (e.g., refund, ATM withdrawal). In the former case, it is
+//! typically constructed by a customer and sent to the merchant who had published the corresponding
+//! offer. In the latter case, an offer doesn't exist as a precursor to the request. Rather the
+//! merchant would typically construct the invoice request and present it to the customer.
+//!
+//! The recipient of the request responds with an `Invoice`.
+//!
+//! ```ignore
+//! extern crate bitcoin;
+//! extern crate lightning;
+//!
+//! use bitcoin::network::constants::Network;
+//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+//! use core::convert::Infallible;
+//! use lightning::ln::features::OfferFeatures;
+//! use lightning::offers::offer::Offer;
+//! use lightning::util::ser::Writeable;
+//!
+//! # fn parse() -> Result<(), lightning::offers::parse::ParseError> {
+//! let secp_ctx = Secp256k1::new();
+//! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
+//! let pubkey = PublicKey::from(keys);
+//! let mut buffer = Vec::new();
+//!
+//! // "offer to be paid" flow
+//! "lno1qcp4256ypq"
+//!     .parse::<Offer>()?
+//!     .request_invoice(vec![42; 64], pubkey)?
+//!     .chain(Network::Testnet)?
+//!     .amount_msats(1000)?
+//!     .quantity(5)?
+//!     .payer_note("foo".to_string())
+//!     .build()?
+//!     .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+//!     .expect("failed verifying signature")
+//!     .write(&mut buffer)
+//!     .unwrap();
+//! # Ok(())
+//! # }
+//! ```
+
+use bitcoin::blockdata::constants::ChainHash;
+use bitcoin::network::constants::Network;
+use bitcoin::secp256k1::{Message, PublicKey};
+use bitcoin::secp256k1::schnorr::Signature;
+use core::convert::TryFrom;
+use crate::io;
+use crate::ln::features::InvoiceRequestFeatures;
+use crate::ln::msgs::DecodeError;
+use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, self};
+use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
+use crate::offers::parse::{ParseError, ParsedMessage, SemanticError};
+use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
+use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
+use crate::util::string::PrintableString;
+
+use crate::prelude::*;
+
+const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature");
+
+/// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [module-level documentation]: self
+pub struct InvoiceRequestBuilder<'a> {
+       offer: &'a Offer,
+       invoice_request: InvoiceRequestContents,
+}
+
+impl<'a> InvoiceRequestBuilder<'a> {
+       pub(super) fn new(offer: &'a Offer, metadata: Vec<u8>, payer_id: PublicKey) -> Self {
+               Self {
+                       offer,
+                       invoice_request: InvoiceRequestContents {
+                               payer: PayerContents(metadata), offer: offer.contents.clone(), chain: None,
+                               amount_msats: None, features: InvoiceRequestFeatures::empty(), quantity: None,
+                               payer_id, payer_note: None,
+                       },
+               }
+       }
+
+       /// Sets the [`InvoiceRequest::chain`] of the given [`Network`] for paying an invoice. If not
+       /// called, [`Network::Bitcoin`] is assumed. Errors if the chain for `network` is not supported
+       /// by the offer.
+       ///
+       /// Successive calls to this method will override the previous setting.
+       pub fn chain(mut self, network: Network) -> Result<Self, SemanticError> {
+               let chain = ChainHash::using_genesis_block(network);
+               if !self.offer.supports_chain(chain) {
+                       return Err(SemanticError::UnsupportedChain);
+               }
+
+               self.invoice_request.chain = Some(chain);
+               Ok(self)
+       }
+
+       /// Sets the [`InvoiceRequest::amount_msats`] for paying an invoice. Errors if `amount_msats` is
+       /// not at least the expected invoice amount (i.e., [`Offer::amount`] times [`quantity`]).
+       ///
+       /// Successive calls to this method will override the previous setting.
+       ///
+       /// [`quantity`]: Self::quantity
+       pub fn amount_msats(mut self, amount_msats: u64) -> Result<Self, SemanticError> {
+               self.invoice_request.offer.check_amount_msats_for_quantity(
+                       Some(amount_msats), self.invoice_request.quantity
+               )?;
+               self.invoice_request.amount_msats = Some(amount_msats);
+               Ok(self)
+       }
+
+       /// Sets [`InvoiceRequest::quantity`] of items. If not set, `1` is assumed. Errors if `quantity`
+       /// does not conform to [`Offer::is_valid_quantity`].
+       ///
+       /// Successive calls to this method will override the previous setting.
+       pub fn quantity(mut self, quantity: u64) -> Result<Self, SemanticError> {
+               self.invoice_request.offer.check_quantity(Some(quantity))?;
+               self.invoice_request.quantity = Some(quantity);
+               Ok(self)
+       }
+
+       /// Sets the [`InvoiceRequest::payer_note`].
+       ///
+       /// Successive calls to this method will override the previous setting.
+       pub fn payer_note(mut self, payer_note: String) -> Self {
+               self.invoice_request.payer_note = Some(payer_note);
+               self
+       }
+
+       /// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed
+       /// by [`UnsignedInvoiceRequest::sign`].
+       pub fn build(mut self) -> Result<UnsignedInvoiceRequest<'a>, SemanticError> {
+               #[cfg(feature = "std")] {
+                       if self.offer.is_expired() {
+                               return Err(SemanticError::AlreadyExpired);
+                       }
+               }
+
+               let chain = self.invoice_request.chain();
+               if !self.offer.supports_chain(chain) {
+                       return Err(SemanticError::UnsupportedChain);
+               }
+
+               if chain == self.offer.implied_chain() {
+                       self.invoice_request.chain = None;
+               }
+
+               if self.offer.amount().is_none() && self.invoice_request.amount_msats.is_none() {
+                       return Err(SemanticError::MissingAmount);
+               }
+
+               self.invoice_request.offer.check_quantity(self.invoice_request.quantity)?;
+               self.invoice_request.offer.check_amount_msats_for_quantity(
+                       self.invoice_request.amount_msats, self.invoice_request.quantity
+               )?;
+
+               let InvoiceRequestBuilder { offer, invoice_request } = self;
+               Ok(UnsignedInvoiceRequest { offer, invoice_request })
+       }
+}
+
+#[cfg(test)]
+impl<'a> InvoiceRequestBuilder<'a> {
+       fn chain_unchecked(mut self, network: Network) -> Self {
+               let chain = ChainHash::using_genesis_block(network);
+               self.invoice_request.chain = Some(chain);
+               self
+       }
+
+       fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
+               self.invoice_request.amount_msats = Some(amount_msats);
+               self
+       }
+
+       fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
+               self.invoice_request.features = features;
+               self
+       }
+
+       fn quantity_unchecked(mut self, quantity: u64) -> Self {
+               self.invoice_request.quantity = Some(quantity);
+               self
+       }
+
+       pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest<'a> {
+               let InvoiceRequestBuilder { offer, invoice_request } = self;
+               UnsignedInvoiceRequest { offer, invoice_request }
+       }
+}
+
+/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
+pub struct UnsignedInvoiceRequest<'a> {
+       offer: &'a Offer,
+       invoice_request: InvoiceRequestContents,
+}
+
+impl<'a> UnsignedInvoiceRequest<'a> {
+       /// Signs the invoice request using the given function.
+       pub fn sign<F, E>(self, sign: F) -> Result<InvoiceRequest, SignError<E>>
+       where
+               F: FnOnce(&Message) -> Result<Signature, E>
+       {
+               // Use the offer bytes instead of the offer TLV stream as the offer may have contained
+               // unknown TLV records, which are not stored in `OfferContents`.
+               let (payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) =
+                       self.invoice_request.as_tlv_stream();
+               let offer_bytes = WithoutLength(&self.offer.bytes);
+               let unsigned_tlv_stream = (payer_tlv_stream, offer_bytes, invoice_request_tlv_stream);
+
+               let mut bytes = Vec::new();
+               unsigned_tlv_stream.write(&mut bytes).unwrap();
+
+               let pubkey = self.invoice_request.payer_id;
+               let signature = Some(merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?);
+
+               // Append the signature TLV record to the bytes.
+               let signature_tlv_stream = SignatureTlvStreamRef {
+                       signature: signature.as_ref(),
+               };
+               signature_tlv_stream.write(&mut bytes).unwrap();
+
+               Ok(InvoiceRequest {
+                       bytes,
+                       contents: self.invoice_request,
+                       signature,
+               })
+       }
+}
+
+/// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`].
+///
+/// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request
+/// specifies these such that its recipient can send an invoice for payment.
+///
+/// [`Offer`]: crate::offers::offer::Offer
+#[derive(Clone, Debug)]
+pub struct InvoiceRequest {
+       pub(super) bytes: Vec<u8>,
+       contents: InvoiceRequestContents,
+       signature: Option<Signature>,
+}
+
+/// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
+#[derive(Clone, Debug)]
+pub(super) struct InvoiceRequestContents {
+       payer: PayerContents,
+       offer: OfferContents,
+       chain: Option<ChainHash>,
+       amount_msats: Option<u64>,
+       features: InvoiceRequestFeatures,
+       quantity: Option<u64>,
+       payer_id: PublicKey,
+       payer_note: Option<String>,
+}
+
+impl InvoiceRequest {
+       /// An unpredictable series of bytes, typically containing information about the derivation of
+       /// [`payer_id`].
+       ///
+       /// [`payer_id`]: Self::payer_id
+       pub fn metadata(&self) -> &[u8] {
+               &self.contents.payer.0[..]
+       }
+
+       /// A chain from [`Offer::chains`] that the offer is valid for.
+       pub fn chain(&self) -> ChainHash {
+               self.contents.chain()
+       }
+
+       /// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
+       /// must be greater than or equal to [`Offer::amount`], converted if necessary.
+       ///
+       /// [`chain`]: Self::chain
+       pub fn amount_msats(&self) -> Option<u64> {
+               self.contents.amount_msats
+       }
+
+       /// Features for paying the invoice.
+       pub fn features(&self) -> &InvoiceRequestFeatures {
+               &self.contents.features
+       }
+
+       /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
+       pub fn quantity(&self) -> Option<u64> {
+               self.contents.quantity
+       }
+
+       /// A possibly transient pubkey used to sign the invoice request.
+       pub fn payer_id(&self) -> PublicKey {
+               self.contents.payer_id
+       }
+
+       /// A payer-provided note which will be seen by the recipient and reflected back in the invoice
+       /// response.
+       pub fn payer_note(&self) -> Option<PrintableString> {
+               self.contents.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
+       }
+
+       /// Signature of the invoice request using [`payer_id`].
+       ///
+       /// [`payer_id`]: Self::payer_id
+       pub fn signature(&self) -> Option<Signature> {
+               self.signature
+       }
+
+       #[cfg(test)]
+       fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
+               let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
+                       self.contents.as_tlv_stream();
+               let signature_tlv_stream = SignatureTlvStreamRef {
+                       signature: self.signature.as_ref(),
+               };
+               (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
+       }
+}
+
+impl InvoiceRequestContents {
+       fn chain(&self) -> ChainHash {
+               self.chain.unwrap_or_else(|| self.offer.implied_chain())
+       }
+
+       pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
+               let payer = PayerTlvStreamRef {
+                       metadata: Some(&self.payer.0),
+               };
+
+               let offer = self.offer.as_tlv_stream();
+
+               let features = {
+                       if self.features == InvoiceRequestFeatures::empty() { None }
+                       else { Some(&self.features) }
+               };
+
+               let invoice_request = InvoiceRequestTlvStreamRef {
+                       chain: self.chain.as_ref(),
+                       amount: self.amount_msats,
+                       features,
+                       quantity: self.quantity,
+                       payer_id: Some(&self.payer_id),
+                       payer_note: self.payer_note.as_ref(),
+               };
+
+               (payer, offer, invoice_request)
+       }
+}
+
+impl Writeable for InvoiceRequest {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+               WithoutLength(&self.bytes).write(writer)
+       }
+}
+
+impl Writeable for InvoiceRequestContents {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+               self.as_tlv_stream().write(writer)
+       }
+}
+
+tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
+       (80, chain: ChainHash),
+       (82, amount: (u64, HighZeroBytesDroppedBigSize)),
+       (84, features: InvoiceRequestFeatures),
+       (86, quantity: (u64, HighZeroBytesDroppedBigSize)),
+       (88, payer_id: PublicKey),
+       (89, payer_note: (String, WithoutLength)),
+});
+
+type FullInvoiceRequestTlvStream =
+       (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream);
+
+#[cfg(test)]
+type FullInvoiceRequestTlvStreamRef<'a> = (
+       PayerTlvStreamRef<'a>,
+       OfferTlvStreamRef<'a>,
+       InvoiceRequestTlvStreamRef<'a>,
+       SignatureTlvStreamRef<'a>,
+);
+
+impl SeekReadable for FullInvoiceRequestTlvStream {
+       fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
+               let payer = SeekReadable::read(r)?;
+               let offer = SeekReadable::read(r)?;
+               let invoice_request = SeekReadable::read(r)?;
+               let signature = SeekReadable::read(r)?;
+
+               Ok((payer, offer, invoice_request, signature))
+       }
+}
+
+type PartialInvoiceRequestTlvStream = (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream);
+
+type PartialInvoiceRequestTlvStreamRef<'a> = (
+       PayerTlvStreamRef<'a>,
+       OfferTlvStreamRef<'a>,
+       InvoiceRequestTlvStreamRef<'a>,
+);
+
+impl TryFrom<Vec<u8>> for InvoiceRequest {
+       type Error = ParseError;
+
+       fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+               let invoice_request = ParsedMessage::<FullInvoiceRequestTlvStream>::try_from(bytes)?;
+               let ParsedMessage { bytes, tlv_stream } = invoice_request;
+               let (
+                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+                       SignatureTlvStream { signature },
+               ) = tlv_stream;
+               let contents = InvoiceRequestContents::try_from(
+                       (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+               )?;
+
+               if let Some(signature) = &signature {
+                       merkle::verify_signature(signature, SIGNATURE_TAG, &bytes, contents.payer_id)?;
+               }
+
+               Ok(InvoiceRequest { bytes, contents, signature })
+       }
+}
+
+impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
+       type Error = SemanticError;
+
+       fn try_from(tlv_stream: PartialInvoiceRequestTlvStream) -> Result<Self, Self::Error> {
+               let (
+                       PayerTlvStream { metadata },
+                       offer_tlv_stream,
+                       InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
+               ) = tlv_stream;
+
+               let payer = match metadata {
+                       None => return Err(SemanticError::MissingPayerMetadata),
+                       Some(metadata) => PayerContents(metadata),
+               };
+               let offer = OfferContents::try_from(offer_tlv_stream)?;
+
+               if !offer.supports_chain(chain.unwrap_or_else(|| offer.implied_chain())) {
+                       return Err(SemanticError::UnsupportedChain);
+               }
+
+               if offer.amount().is_none() && amount.is_none() {
+                       return Err(SemanticError::MissingAmount);
+               }
+
+               offer.check_quantity(quantity)?;
+               offer.check_amount_msats_for_quantity(amount, quantity)?;
+
+               let features = features.unwrap_or_else(InvoiceRequestFeatures::empty);
+
+               let payer_id = match payer_id {
+                       None => return Err(SemanticError::MissingPayerId),
+                       Some(payer_id) => payer_id,
+               };
+
+               Ok(InvoiceRequestContents {
+                       payer, offer, chain, amount_msats: amount, features, quantity, payer_id, payer_note,
+               })
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       use super::InvoiceRequest;
+
+       use bitcoin::blockdata::constants::ChainHash;
+       use bitcoin::network::constants::Network;
+       use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, SecretKey, self};
+       use bitcoin::secp256k1::schnorr::Signature;
+       use core::convert::{Infallible, TryFrom};
+       use core::num::NonZeroU64;
+       #[cfg(feature = "std")]
+       use core::time::Duration;
+       use crate::ln::features::InvoiceRequestFeatures;
+       use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+       use crate::offers::merkle::SignError;
+       use crate::offers::offer::{Amount, OfferBuilder, Quantity};
+       use crate::offers::parse::{ParseError, SemanticError};
+       use crate::util::ser::{BigSize, Writeable};
+       use crate::util::string::PrintableString;
+
+       fn payer_keys() -> KeyPair {
+               let secp_ctx = Secp256k1::new();
+               KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
+       }
+
+       fn payer_sign(digest: &Message) -> Result<Signature, Infallible> {
+               let secp_ctx = Secp256k1::new();
+               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+       }
+
+       fn payer_pubkey() -> PublicKey {
+               payer_keys().public_key()
+       }
+
+       fn recipient_sign(digest: &Message) -> Result<Signature, Infallible> {
+               let secp_ctx = Secp256k1::new();
+               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap());
+               Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+       }
+
+       fn recipient_pubkey() -> PublicKey {
+               let secp_ctx = Secp256k1::new();
+               KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap()).public_key()
+       }
+
+       #[test]
+       fn builds_invoice_request_with_defaults() {
+               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap();
+               let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap().sign(payer_sign).unwrap();
+
+               let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream) =
+                       invoice_request.as_tlv_stream();
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               assert_eq!(invoice_request.bytes, buffer.as_slice());
+               assert_eq!(invoice_request.metadata(), &[1; 32]);
+               assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
+               assert_eq!(invoice_request.amount_msats(), None);
+               assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+               assert_eq!(invoice_request.quantity(), None);
+               assert_eq!(invoice_request.payer_id(), payer_pubkey());
+               assert_eq!(invoice_request.payer_note(), None);
+               assert!(invoice_request.signature().is_some());
+
+               assert_eq!(payer_tlv_stream.metadata, Some(&vec![1; 32]));
+               assert_eq!(offer_tlv_stream.chains, None);
+               assert_eq!(offer_tlv_stream.metadata, None);
+               assert_eq!(offer_tlv_stream.currency, None);
+               assert_eq!(offer_tlv_stream.amount, Some(1000));
+               assert_eq!(offer_tlv_stream.description, Some(&String::from("foo")));
+               assert_eq!(offer_tlv_stream.features, None);
+               assert_eq!(offer_tlv_stream.absolute_expiry, None);
+               assert_eq!(offer_tlv_stream.paths, None);
+               assert_eq!(offer_tlv_stream.issuer, None);
+               assert_eq!(offer_tlv_stream.quantity_max, None);
+               assert_eq!(offer_tlv_stream.node_id, Some(&recipient_pubkey()));
+               assert_eq!(invoice_request_tlv_stream.chain, None);
+               assert_eq!(invoice_request_tlv_stream.amount, None);
+               assert_eq!(invoice_request_tlv_stream.features, None);
+               assert_eq!(invoice_request_tlv_stream.quantity, None);
+               assert_eq!(invoice_request_tlv_stream.payer_id, Some(&payer_pubkey()));
+               assert_eq!(invoice_request_tlv_stream.payer_note, None);
+               assert!(signature_tlv_stream.signature.is_some());
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice request: {:?}", e);
+               }
+       }
+
+       #[cfg(feature = "std")]
+       #[test]
+       fn builds_invoice_request_from_offer_with_expiration() {
+               let future_expiry = Duration::from_secs(u64::max_value());
+               let past_expiry = Duration::from_secs(0);
+
+               if let Err(e) = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .absolute_expiry(future_expiry)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build()
+               {
+                       panic!("error building invoice_request: {:?}", e);
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .absolute_expiry(past_expiry)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build()
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::AlreadyExpired),
+               }
+       }
+
+       #[test]
+       fn builds_invoice_request_with_chain() {
+               let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
+               let testnet = ChainHash::using_genesis_block(Network::Testnet);
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Bitcoin).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.chain(), mainnet);
+               assert_eq!(tlv_stream.chain, None);
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .chain(Network::Testnet)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Testnet).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.chain(), testnet);
+               assert_eq!(tlv_stream.chain, Some(&testnet));
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .chain(Network::Bitcoin)
+                       .chain(Network::Testnet)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Bitcoin).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.chain(), mainnet);
+               assert_eq!(tlv_stream.chain, None);
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .chain(Network::Bitcoin)
+                       .chain(Network::Testnet)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Bitcoin).unwrap()
+                       .chain(Network::Testnet).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.chain(), testnet);
+               assert_eq!(tlv_stream.chain, Some(&testnet));
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .chain(Network::Testnet)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Bitcoin)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::UnsupportedChain),
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .chain(Network::Testnet)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build()
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::UnsupportedChain),
+               }
+       }
+
+       #[test]
+       fn builds_invoice_request_with_amount() {
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(1000).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.amount_msats(), Some(1000));
+               assert_eq!(tlv_stream.amount, Some(1000));
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(1001).unwrap()
+                       .amount_msats(1000).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.amount_msats(), Some(1000));
+               assert_eq!(tlv_stream.amount, Some(1000));
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(1001).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.amount_msats(), Some(1001));
+               assert_eq!(tlv_stream.amount, Some(1001));
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(999)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::InsufficientAmount),
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .quantity(2).unwrap()
+                       .amount_msats(1000)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::InsufficientAmount),
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(MAX_VALUE_MSAT + 1)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::InvalidAmount),
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(1000).unwrap()
+                       .quantity(2).unwrap()
+                       .build()
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::InsufficientAmount),
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build()
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::MissingAmount),
+               }
+       }
+
+       #[test]
+       fn builds_invoice_request_with_features() {
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .features_unchecked(InvoiceRequestFeatures::unknown())
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::unknown());
+               assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown()));
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .features_unchecked(InvoiceRequestFeatures::unknown())
+                       .features_unchecked(InvoiceRequestFeatures::empty())
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+               assert_eq!(tlv_stream.features, None);
+       }
+
+       #[test]
+       fn builds_invoice_request_with_quantity() {
+               let ten = NonZeroU64::new(10).unwrap();
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::one())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.quantity(), None);
+               assert_eq!(tlv_stream.quantity, None);
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::one())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(2_000).unwrap()
+                       .quantity(2)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::UnexpectedQuantity),
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Bounded(ten))
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(10_000).unwrap()
+                       .quantity(10).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.amount_msats(), Some(10_000));
+               assert_eq!(tlv_stream.amount, Some(10_000));
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Bounded(ten))
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(11_000).unwrap()
+                       .quantity(11)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::InvalidQuantity),
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(2_000).unwrap()
+                       .quantity(2).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.amount_msats(), Some(2_000));
+               assert_eq!(tlv_stream.amount, Some(2_000));
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build()
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::MissingQuantity),
+               }
+       }
+
+       #[test]
+       fn builds_invoice_request_with_payer_note() {
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .payer_note("bar".into())
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.payer_note(), Some(PrintableString("bar")));
+               assert_eq!(tlv_stream.payer_note, Some(&String::from("bar")));
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .payer_note("bar".into())
+                       .payer_note("baz".into())
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+               assert_eq!(invoice_request.payer_note(), Some(PrintableString("baz")));
+               assert_eq!(tlv_stream.payer_note, Some(&String::from("baz")));
+       }
+
+       #[test]
+       fn fails_signing_invoice_request() {
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(|_| Err(()))
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SignError::Signing(())),
+               }
+
+               match OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(recipient_sign)
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SignError::Verification(secp256k1::Error::InvalidSignature)),
+               }
+       }
+
+       #[test]
+       fn parses_invoice_request_with_metadata() {
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![42; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+       }
+
+       #[test]
+       fn parses_invoice_request_with_chain() {
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Bitcoin).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain_unchecked(Network::Testnet)
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedChain)),
+               }
+       }
+
+       #[test]
+       fn parses_invoice_request_with_amount() {
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(1000).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingAmount)),
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats_unchecked(999)
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InsufficientAmount)),
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount(Amount::Currency { iso4217_code: *b"USD", amount: 1000 })
+                       .build_unchecked()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedCurrency));
+                       },
+               }
+       }
+
+       #[test]
+       fn parses_invoice_request_with_quantity() {
+               let ten = NonZeroU64::new(10).unwrap();
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::one())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::one())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(2_000).unwrap()
+                       .quantity_unchecked(2)
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnexpectedQuantity));
+                       },
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Bounded(ten))
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(10_000).unwrap()
+                       .quantity(10).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Bounded(ten))
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(11_000).unwrap()
+                       .quantity_unchecked(11)
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InvalidQuantity)),
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .amount_msats(2_000).unwrap()
+                       .quantity(2).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+
+               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build_unchecked()
+                       .sign(payer_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingQuantity)),
+               }
+       }
+
+       #[test]
+       fn fails_parsing_invoice_request_without_metadata() {
+               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap();
+               let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap();
+               let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+               tlv_stream.0.metadata = None;
+
+               let mut buffer = Vec::new();
+               tlv_stream.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingPayerMetadata));
+                       },
+               }
+       }
+
+       #[test]
+       fn fails_parsing_invoice_request_without_payer_id() {
+               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap();
+               let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap();
+               let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+               tlv_stream.2.payer_id = None;
+
+               let mut buffer = Vec::new();
+               tlv_stream.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingPayerId)),
+               }
+       }
+
+       #[test]
+       fn fails_parsing_invoice_request_without_node_id() {
+               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap();
+               let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap();
+               let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+               tlv_stream.1.node_id = None;
+
+               let mut buffer = Vec::new();
+               tlv_stream.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSigningPubkey));
+                       },
+               }
+       }
+
+       #[test]
+       fn parses_invoice_request_without_signature() {
+               let mut buffer = Vec::new();
+               OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .invoice_request
+                       .write(&mut buffer).unwrap();
+
+               if let Err(e) = InvoiceRequest::try_from(buffer) {
+                       panic!("error parsing invoice_request: {:?}", e);
+               }
+       }
+
+       #[test]
+       fn fails_parsing_invoice_request_with_invalid_signature() {
+               let mut invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               let last_signature_byte = invoice_request.bytes.last_mut().unwrap();
+               *last_signature_byte = last_signature_byte.wrapping_add(1);
+
+               let mut buffer = Vec::new();
+               invoice_request.write(&mut buffer).unwrap();
+
+               match InvoiceRequest::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, ParseError::InvalidSignature(secp256k1::Error::InvalidSignature));
+                       },
+               }
+       }
+
+       #[test]
+       fn fails_parsing_invoice_request_with_extra_tlv_records() {
+               let secp_ctx = Secp256k1::new();
+               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let invoice_request = OfferBuilder::new("foo".into(), keys.public_key())
+                       .amount_msats(1000)
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], keys.public_key()).unwrap()
+                       .build().unwrap()
+                       .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+                       .unwrap();
+
+               let mut encoded_invoice_request = Vec::new();
+               invoice_request.write(&mut encoded_invoice_request).unwrap();
+               BigSize(1002).write(&mut encoded_invoice_request).unwrap();
+               BigSize(32).write(&mut encoded_invoice_request).unwrap();
+               [42u8; 32].write(&mut encoded_invoice_request).unwrap();
+
+               match InvoiceRequest::try_from(encoded_invoice_request) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, ParseError::Decode(DecodeError::InvalidValue)),
+               }
+       }
+}
diff --git a/lightning/src/offers/merkle.rs b/lightning/src/offers/merkle.rs
new file mode 100644 (file)
index 0000000..57e7fe6
--- /dev/null
@@ -0,0 +1,272 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Tagged hashes for use in signature calculation and verification.
+
+use bitcoin::hashes::{Hash, HashEngine, sha256};
+use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, self};
+use bitcoin::secp256k1::schnorr::Signature;
+use crate::io;
+use crate::util::ser::{BigSize, Readable};
+
+use crate::prelude::*;
+
+/// Valid type range for signature TLV records.
+const SIGNATURE_TYPES: core::ops::RangeInclusive<u64> = 240..=1000;
+
+tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
+       (240, signature: Signature),
+});
+
+/// Error when signing messages.
+#[derive(Debug, PartialEq)]
+pub enum SignError<E> {
+       /// User-defined error when signing the message.
+       Signing(E),
+       /// Error when verifying the produced signature using the given pubkey.
+       Verification(secp256k1::Error),
+}
+
+/// Signs a message digest consisting of a tagged hash of the given bytes, checking if it can be
+/// verified with the supplied pubkey.
+///
+/// Panics if `bytes` is not a well-formed TLV stream containing at least one TLV record.
+pub(super) fn sign_message<F, E>(
+       sign: F, tag: &str, bytes: &[u8], pubkey: PublicKey,
+) -> Result<Signature, SignError<E>>
+where
+       F: FnOnce(&Message) -> Result<Signature, E>
+{
+       let digest = message_digest(tag, bytes);
+       let signature = sign(&digest).map_err(|e| SignError::Signing(e))?;
+
+       let pubkey = pubkey.into();
+       let secp_ctx = Secp256k1::verification_only();
+       secp_ctx.verify_schnorr(&signature, &digest, &pubkey).map_err(|e| SignError::Verification(e))?;
+
+       Ok(signature)
+}
+
+/// Verifies the signature with a pubkey over the given bytes using a tagged hash as the message
+/// digest.
+///
+/// Panics if `bytes` is not a well-formed TLV stream containing at least one TLV record.
+pub(super) fn verify_signature(
+       signature: &Signature, tag: &str, bytes: &[u8], pubkey: PublicKey,
+) -> Result<(), secp256k1::Error> {
+       let digest = message_digest(tag, bytes);
+       let pubkey = pubkey.into();
+       let secp_ctx = Secp256k1::verification_only();
+       secp_ctx.verify_schnorr(signature, &digest, &pubkey)
+}
+
+fn message_digest(tag: &str, bytes: &[u8]) -> Message {
+       let tag = sha256::Hash::hash(tag.as_bytes());
+       let merkle_root = root_hash(bytes);
+       Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap()
+}
+
+/// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
+/// containing at least one TLV record.
+fn root_hash(data: &[u8]) -> sha256::Hash {
+       let mut tlv_stream = TlvStream::new(&data[..]).peekable();
+       let nonce_tag = tagged_hash_engine(sha256::Hash::from_engine({
+               let mut engine = sha256::Hash::engine();
+               engine.input("LnNonce".as_bytes());
+               engine.input(tlv_stream.peek().unwrap().record_bytes);
+               engine
+       }));
+       let leaf_tag = tagged_hash_engine(sha256::Hash::hash("LnLeaf".as_bytes()));
+       let branch_tag = tagged_hash_engine(sha256::Hash::hash("LnBranch".as_bytes()));
+
+       let mut leaves = Vec::new();
+       for record in tlv_stream {
+               if !SIGNATURE_TYPES.contains(&record.r#type) {
+                       leaves.push(tagged_hash_from_engine(leaf_tag.clone(), &record));
+                       leaves.push(tagged_hash_from_engine(nonce_tag.clone(), &record.type_bytes));
+               }
+       }
+
+       // Calculate the merkle root hash in place.
+       let num_leaves = leaves.len();
+       for level in 0.. {
+               let step = 2 << level;
+               let offset = step / 2;
+               if offset >= num_leaves {
+                       break;
+               }
+
+               let left_branches = (0..num_leaves).step_by(step);
+               let right_branches = (offset..num_leaves).step_by(step);
+               for (i, j) in left_branches.zip(right_branches) {
+                       leaves[i] = tagged_branch_hash_from_engine(branch_tag.clone(), leaves[i], leaves[j]);
+               }
+       }
+
+       *leaves.first().unwrap()
+}
+
+fn tagged_hash<T: AsRef<[u8]>>(tag: sha256::Hash, msg: T) -> sha256::Hash {
+       let engine = tagged_hash_engine(tag);
+       tagged_hash_from_engine(engine, msg)
+}
+
+fn tagged_hash_engine(tag: sha256::Hash) -> sha256::HashEngine {
+       let mut engine = sha256::Hash::engine();
+       engine.input(tag.as_ref());
+       engine.input(tag.as_ref());
+       engine
+}
+
+fn tagged_hash_from_engine<T: AsRef<[u8]>>(mut engine: sha256::HashEngine, msg: T) -> sha256::Hash {
+       engine.input(msg.as_ref());
+       sha256::Hash::from_engine(engine)
+}
+
+fn tagged_branch_hash_from_engine(
+       mut engine: sha256::HashEngine, leaf1: sha256::Hash, leaf2: sha256::Hash,
+) -> sha256::Hash {
+       if leaf1 < leaf2 {
+               engine.input(leaf1.as_ref());
+               engine.input(leaf2.as_ref());
+       } else {
+               engine.input(leaf2.as_ref());
+               engine.input(leaf1.as_ref());
+       };
+       sha256::Hash::from_engine(engine)
+}
+
+/// [`Iterator`] over a sequence of bytes yielding [`TlvRecord`]s. The input is assumed to be a
+/// well-formed TLV stream.
+struct TlvStream<'a> {
+       data: io::Cursor<&'a [u8]>,
+}
+
+impl<'a> TlvStream<'a> {
+       fn new(data: &'a [u8]) -> Self {
+               Self {
+                       data: io::Cursor::new(data),
+               }
+       }
+}
+
+/// A slice into a [`TlvStream`] for a record.
+struct TlvRecord<'a> {
+       r#type: u64,
+       type_bytes: &'a [u8],
+       // The entire TLV record.
+       record_bytes: &'a [u8],
+}
+
+impl AsRef<[u8]> for TlvRecord<'_> {
+       fn as_ref(&self) -> &[u8] { &self.record_bytes }
+}
+
+impl<'a> Iterator for TlvStream<'a> {
+       type Item = TlvRecord<'a>;
+
+       fn next(&mut self) -> Option<Self::Item> {
+               if self.data.position() < self.data.get_ref().len() as u64 {
+                       let start = self.data.position();
+
+                       let r#type = <BigSize as Readable>::read(&mut self.data).unwrap().0;
+                       let offset = self.data.position();
+                       let type_bytes = &self.data.get_ref()[start as usize..offset as usize];
+
+                       let length = <BigSize as Readable>::read(&mut self.data).unwrap().0;
+                       let offset = self.data.position();
+                       let end = offset + length;
+
+                       let _value = &self.data.get_ref()[offset as usize..end as usize];
+                       let record_bytes = &self.data.get_ref()[start as usize..end as usize];
+
+                       self.data.set_position(end);
+
+                       Some(TlvRecord { r#type, type_bytes, record_bytes })
+               } else {
+                       None
+               }
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       use bitcoin::hashes::{Hash, sha256};
+       use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey};
+       use core::convert::Infallible;
+       use crate::offers::offer::{Amount, OfferBuilder};
+       use crate::offers::invoice_request::InvoiceRequest;
+       use crate::offers::parse::Bech32Encode;
+
+       #[test]
+       fn calculates_merkle_root_hash() {
+               // BOLT 12 test vectors
+               macro_rules! tlv1 { () => { "010203e8" } }
+               macro_rules! tlv2 { () => { "02080000010000020003" } }
+               macro_rules! tlv3 { () => { "03310266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351800000000000000010000000000000002" } }
+               assert_eq!(
+                       super::root_hash(&hex::decode(tlv1!()).unwrap()),
+                       sha256::Hash::from_slice(&hex::decode("b013756c8fee86503a0b4abdab4cddeb1af5d344ca6fc2fa8b6c08938caa6f93").unwrap()).unwrap(),
+               );
+               assert_eq!(
+                       super::root_hash(&hex::decode(concat!(tlv1!(), tlv2!())).unwrap()),
+                       sha256::Hash::from_slice(&hex::decode("c3774abbf4815aa54ccaa026bff6581f01f3be5fe814c620a252534f434bc0d1").unwrap()).unwrap(),
+               );
+               assert_eq!(
+                       super::root_hash(&hex::decode(concat!(tlv1!(), tlv2!(), tlv3!())).unwrap()),
+                       sha256::Hash::from_slice(&hex::decode("ab2e79b1283b0b31e0b035258de23782df6b89a38cfa7237bde69aed1a658c5d").unwrap()).unwrap(),
+               );
+       }
+
+       #[test]
+       fn calculates_merkle_root_hash_from_invoice_request() {
+               let secp_ctx = Secp256k1::new();
+               let recipient_pubkey = {
+                       let secret_key = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()).unwrap();
+                       KeyPair::from_secret_key(&secp_ctx, &secret_key).public_key()
+               };
+               let payer_keys = {
+                       let secret_key = SecretKey::from_slice(&hex::decode("4242424242424242424242424242424242424242424242424242424242424242").unwrap()).unwrap();
+                       KeyPair::from_secret_key(&secp_ctx, &secret_key)
+               };
+
+               // BOLT 12 test vectors
+               let invoice_request = OfferBuilder::new("A Mathematical Treatise".into(), recipient_pubkey)
+                       .amount(Amount::Currency { iso4217_code: *b"USD", amount: 100 })
+                       .build_unchecked()
+                       .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
+                       .build_unchecked()
+                       .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &payer_keys)))
+                       .unwrap();
+               assert_eq!(
+                       invoice_request.to_string(),
+                       "lnr1qqyqqqqqqqqqqqqqqcp4256ypqqkgzshgysy6ct5dpjk6ct5d93kzmpq23ex2ct5d9ek293pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpjkppqvjx204vgdzgsqpvcp4mldl3plscny0rt707gvpdh6ndydfacz43euzqhrurageg3n7kafgsek6gz3e9w52parv8gs2hlxzk95tzeswywffxlkeyhml0hh46kndmwf4m6xma3tkq2lu04qz3slje2rfthc89vss",
+               );
+               assert_eq!(
+                       super::root_hash(&invoice_request.bytes[..]),
+                       sha256::Hash::from_slice(&hex::decode("608407c18ad9a94d9ea2bcdbe170b6c20c462a7833a197621c916f78cf18e624").unwrap()).unwrap(),
+               );
+       }
+
+       impl AsRef<[u8]> for InvoiceRequest {
+               fn as_ref(&self) -> &[u8] {
+                       &self.bytes
+               }
+       }
+
+       impl Bech32Encode for InvoiceRequest {
+               const BECH32_HRP: &'static str = "lnr";
+       }
+
+       impl core::fmt::Display for InvoiceRequest {
+               fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+                       self.fmt_bech32_str(f)
+               }
+       }
+}
index 273650285c6e4b9f084d1ed5bb260dafdadd8562..be0eb2da522c3f260bec2db2b370ed6a3bf2711c 100644 (file)
@@ -12,5 +12,8 @@
 //!
 //! Offers are a flexible protocol for Lightning payments.
 
+pub mod invoice_request;
+mod merkle;
 pub mod offer;
 pub mod parse;
+mod payer;
index 704045f760b02867e094026a3a0e87816bd82189..680f4094162ff49021bd47f1a705ff6f154cb375 100644 (file)
@@ -76,6 +76,7 @@ use core::time::Duration;
 use crate::io;
 use crate::ln::features::OfferFeatures;
 use crate::ln::msgs::MAX_VALUE_MSAT;
+use crate::offers::invoice_request::InvoiceRequestBuilder;
 use crate::offers::parse::{Bech32Encode, ParseError, ParsedMessage, SemanticError};
 use crate::onion_message::BlindedPath;
 use crate::util::ser::{HighZeroBytesDroppedBigSize, WithoutLength, Writeable, Writer};
@@ -137,27 +138,18 @@ impl OfferBuilder {
        /// Sets the [`Offer::amount`] as an [`Amount::Bitcoin`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn amount_msats(mut self, amount_msats: u64) -> Self {
+       pub fn amount_msats(self, amount_msats: u64) -> Self {
                self.amount(Amount::Bitcoin { amount_msats })
        }
 
        /// Sets the [`Offer::amount`].
        ///
        /// Successive calls to this method will override the previous setting.
-       fn amount(mut self, amount: Amount) -> Self {
+       pub(super) fn amount(mut self, amount: Amount) -> Self {
                self.offer.amount = Some(amount);
                self
        }
 
-       /// Sets the [`Offer::features`].
-       ///
-       /// Successive calls to this method will override the previous setting.
-       #[cfg(test)]
-       pub fn features(mut self, features: OfferFeatures) -> Self {
-               self.offer.features = features;
-               self
-       }
-
        /// Sets the [`Offer::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
        /// already passed is valid and can be checked for using [`Offer::is_expired`].
        ///
@@ -222,9 +214,24 @@ impl OfferBuilder {
        }
 }
 
+#[cfg(test)]
+impl OfferBuilder {
+       fn features_unchecked(mut self, features: OfferFeatures) -> Self {
+               self.offer.features = features;
+               self
+       }
+
+       pub(super) fn build_unchecked(self) -> Offer {
+               let mut bytes = Vec::new();
+               self.offer.write(&mut bytes).unwrap();
+
+               Offer { bytes, contents: self.offer }
+       }
+}
+
 /// An `Offer` is a potentially long-lived proposal for payment of a good or service.
 ///
-/// An offer is a precursor to an `InvoiceRequest`. A merchant publishes an offer from which a
+/// An offer is a precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a
 /// customer may request an `Invoice` for a specific quantity and using an amount sufficient to
 /// cover that quantity (i.e., at least `quantity * amount`). See [`Offer::amount`].
 ///
@@ -232,17 +239,21 @@ impl OfferBuilder {
 /// latter.
 ///
 /// Through the use of [`BlindedPath`]s, offers provide recipient privacy.
+///
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 #[derive(Clone, Debug)]
 pub struct Offer {
        // The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
        // fields.
-       bytes: Vec<u8>,
-       contents: OfferContents,
+       pub(super) bytes: Vec<u8>,
+       pub(super) contents: OfferContents,
 }
 
-/// The contents of an [`Offer`], which may be shared with an `InvoiceRequest` or an `Invoice`.
+/// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or an `Invoice`.
+///
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 #[derive(Clone, Debug)]
-pub(crate) struct OfferContents {
+pub(super) struct OfferContents {
        chains: Option<Vec<ChainHash>>,
        metadata: Option<Vec<u8>>,
        amount: Option<Amount>,
@@ -263,10 +274,16 @@ impl Offer {
        /// Payments must be denominated in units of the minimal lightning-payable unit (e.g., msats)
        /// for the selected chain.
        pub fn chains(&self) -> Vec<ChainHash> {
-               self.contents.chains
-                       .as_ref()
-                       .cloned()
-                       .unwrap_or_else(|| vec![self.contents.implied_chain()])
+               self.contents.chains()
+       }
+
+       pub(super) fn implied_chain(&self) -> ChainHash {
+               self.contents.implied_chain()
+       }
+
+       /// Returns whether the given chain is supported by the offer.
+       pub fn supports_chain(&self, chain: ChainHash) -> bool {
+               self.contents.supports_chain(chain)
        }
 
        // TODO: Link to corresponding method in `InvoiceRequest`.
@@ -278,7 +295,7 @@ impl Offer {
 
        /// The minimum amount required for a successful payment of a single item.
        pub fn amount(&self) -> Option<&Amount> {
-               self.contents.amount.as_ref()
+               self.contents.amount()
        }
 
        /// A complete description of the purpose of the payment. Intended to be displayed to the user
@@ -328,13 +345,48 @@ impl Offer {
                self.contents.supported_quantity()
        }
 
+       /// Returns whether the given quantity is valid for the offer.
+       pub fn is_valid_quantity(&self, quantity: u64) -> bool {
+               self.contents.is_valid_quantity(quantity)
+       }
+
+       /// Returns whether a quantity is expected in an [`InvoiceRequest`] for the offer.
+       ///
+       /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+       pub fn expects_quantity(&self) -> bool {
+               self.contents.expects_quantity()
+       }
+
        /// The public key used by the recipient to sign invoices.
        pub fn signing_pubkey(&self) -> PublicKey {
                self.contents.signing_pubkey.unwrap()
        }
 
+       /// Creates an [`InvoiceRequest`] for the offer with the given `metadata` and `payer_id`, which
+       /// will be reflected in the `Invoice` response.
+       ///
+       /// The `metadata` is useful for including information about the derivation of `payer_id` such
+       /// that invoice response handling can be stateless. Also serves as payer-provided entropy while
+       /// hashing in the signature calculation.
+       ///
+       /// This should not leak any information such as by using a simple BIP-32 derivation path.
+       /// Otherwise, payments may be correlated.
+       ///
+       /// Errors if the offer contains unknown required features.
+       ///
+       /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+       pub fn request_invoice(
+               &self, metadata: Vec<u8>, payer_id: PublicKey
+       ) -> Result<InvoiceRequestBuilder, SemanticError> {
+               if self.features().requires_unknown_bits() {
+                       return Err(SemanticError::UnknownRequiredFeatures);
+               }
+
+               Ok(InvoiceRequestBuilder::new(self, metadata, payer_id))
+       }
+
        #[cfg(test)]
-       fn as_tlv_stream(&self) -> OfferTlvStreamRef {
+       pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
                self.contents.as_tlv_stream()
        }
 }
@@ -346,15 +398,82 @@ impl AsRef<[u8]> for Offer {
 }
 
 impl OfferContents {
+       pub fn chains(&self) -> Vec<ChainHash> {
+               self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
+       }
+
        pub fn implied_chain(&self) -> ChainHash {
                ChainHash::using_genesis_block(Network::Bitcoin)
        }
 
+       pub fn supports_chain(&self, chain: ChainHash) -> bool {
+               self.chains().contains(&chain)
+       }
+
+       pub fn amount(&self) -> Option<&Amount> {
+               self.amount.as_ref()
+       }
+
+       pub(super) fn check_amount_msats_for_quantity(
+               &self, amount_msats: Option<u64>, quantity: Option<u64>
+       ) -> Result<(), SemanticError> {
+               let offer_amount_msats = match self.amount {
+                       None => 0,
+                       Some(Amount::Bitcoin { amount_msats }) => amount_msats,
+                       Some(Amount::Currency { .. }) => return Err(SemanticError::UnsupportedCurrency),
+               };
+
+               if !self.expects_quantity() || quantity.is_some() {
+                       let expected_amount_msats = offer_amount_msats * quantity.unwrap_or(1);
+                       let amount_msats = amount_msats.unwrap_or(expected_amount_msats);
+
+                       if amount_msats < expected_amount_msats {
+                               return Err(SemanticError::InsufficientAmount);
+                       }
+
+                       if amount_msats > MAX_VALUE_MSAT {
+                               return Err(SemanticError::InvalidAmount);
+                       }
+               }
+
+               Ok(())
+       }
+
        pub fn supported_quantity(&self) -> Quantity {
                self.supported_quantity
        }
 
-       fn as_tlv_stream(&self) -> OfferTlvStreamRef {
+       pub(super) fn check_quantity(&self, quantity: Option<u64>) -> Result<(), SemanticError> {
+               let expects_quantity = self.expects_quantity();
+               match quantity {
+                       None if expects_quantity => Err(SemanticError::MissingQuantity),
+                       Some(_) if !expects_quantity => Err(SemanticError::UnexpectedQuantity),
+                       Some(quantity) if !self.is_valid_quantity(quantity) => {
+                               Err(SemanticError::InvalidQuantity)
+                       },
+                       _ => Ok(()),
+               }
+       }
+
+       fn is_valid_quantity(&self, quantity: u64) -> bool {
+               match self.supported_quantity {
+                       Quantity::Bounded(n) => {
+                               let n = n.get();
+                               if n == 1 { false }
+                               else { quantity > 0 && quantity <= n }
+                       },
+                       Quantity::Unbounded => quantity > 0,
+               }
+       }
+
+       fn expects_quantity(&self) -> bool {
+               match self.supported_quantity {
+                       Quantity::Bounded(n) => n.get() != 1,
+                       Quantity::Unbounded => true,
+               }
+       }
+
+       pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
                let (currency, amount) = match &self.amount {
                        None => (None, None),
                        Some(Amount::Bitcoin { amount_msats }) => (None, Some(*amount_msats)),
@@ -567,6 +686,7 @@ mod tests {
 
                assert_eq!(offer.bytes, buffer.as_slice());
                assert_eq!(offer.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
+               assert!(offer.supports_chain(ChainHash::using_genesis_block(Network::Bitcoin)));
                assert_eq!(offer.metadata(), None);
                assert_eq!(offer.amount(), None);
                assert_eq!(offer.description(), PrintableString("foo"));
@@ -605,6 +725,7 @@ mod tests {
                        .chain(Network::Bitcoin)
                        .build()
                        .unwrap();
+               assert!(offer.supports_chain(mainnet));
                assert_eq!(offer.chains(), vec![mainnet]);
                assert_eq!(offer.as_tlv_stream().chains, None);
 
@@ -612,6 +733,7 @@ mod tests {
                        .chain(Network::Testnet)
                        .build()
                        .unwrap();
+               assert!(offer.supports_chain(testnet));
                assert_eq!(offer.chains(), vec![testnet]);
                assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
 
@@ -620,6 +742,7 @@ mod tests {
                        .chain(Network::Testnet)
                        .build()
                        .unwrap();
+               assert!(offer.supports_chain(testnet));
                assert_eq!(offer.chains(), vec![testnet]);
                assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
 
@@ -628,6 +751,8 @@ mod tests {
                        .chain(Network::Testnet)
                        .build()
                        .unwrap();
+               assert!(offer.supports_chain(mainnet));
+               assert!(offer.supports_chain(testnet));
                assert_eq!(offer.chains(), vec![mainnet, testnet]);
                assert_eq!(offer.as_tlv_stream().chains, Some(&vec![mainnet, testnet]));
        }
@@ -694,15 +819,15 @@ mod tests {
        #[test]
        fn builds_offer_with_features() {
                let offer = OfferBuilder::new("foo".into(), pubkey(42))
-                       .features(OfferFeatures::unknown())
+                       .features_unchecked(OfferFeatures::unknown())
                        .build()
                        .unwrap();
                assert_eq!(offer.features(), &OfferFeatures::unknown());
                assert_eq!(offer.as_tlv_stream().features, Some(&OfferFeatures::unknown()));
 
                let offer = OfferBuilder::new("foo".into(), pubkey(42))
-                       .features(OfferFeatures::unknown())
-                       .features(OfferFeatures::empty())
+                       .features_unchecked(OfferFeatures::unknown())
+                       .features_unchecked(OfferFeatures::empty())
                        .build()
                        .unwrap();
                assert_eq!(offer.features(), &OfferFeatures::empty());
@@ -824,6 +949,18 @@ mod tests {
                assert_eq!(tlv_stream.quantity_max, None);
        }
 
+       #[test]
+       fn fails_requesting_invoice_with_unknown_required_features() {
+               match OfferBuilder::new("foo".into(), pubkey(42))
+                       .features_unchecked(OfferFeatures::unknown())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], pubkey(43))
+               {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => assert_eq!(e, SemanticError::UnknownRequiredFeatures),
+               }
+       }
+
        #[test]
        fn parses_offer_with_chains() {
                let offer = OfferBuilder::new("foo".into(), pubkey(42))
index 19d7d74ed6111fd6ddc9fef9d7410a8a47b43354..0b3dda7928593871f8007c5ff50d2d081366dac8 100644 (file)
@@ -11,6 +11,7 @@
 
 use bitcoin::bech32;
 use bitcoin::bech32::{FromBase32, ToBase32};
+use bitcoin::secp256k1;
 use core::convert::TryFrom;
 use core::fmt;
 use crate::io;
@@ -20,7 +21,7 @@ use crate::util::ser::SeekReadable;
 use crate::prelude::*;
 
 /// Indicates a message can be encoded using bech32.
-pub(crate) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
+pub(super) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
        /// Human readable part of the message's bech32 encoding.
        const BECH32_HRP: &'static str;
 
@@ -78,7 +79,7 @@ impl<'a> AsRef<str> for Bech32String<'a> {
 
 /// A wrapper for reading a message as a TLV stream `T` from a byte sequence, while still
 /// maintaining ownership of the bytes for later use.
-pub(crate) struct ParsedMessage<T: SeekReadable> {
+pub(super) struct ParsedMessage<T: SeekReadable> {
        pub bytes: Vec<u8>,
        pub tlv_stream: T,
 }
@@ -115,23 +116,41 @@ pub enum ParseError {
        Decode(DecodeError),
        /// The parsed message has invalid semantics.
        InvalidSemantics(SemanticError),
+       /// The parsed message has an invalid signature.
+       InvalidSignature(secp256k1::Error),
 }
 
 /// Error when interpreting a TLV stream as a specific type.
 #[derive(Debug, PartialEq)]
 pub enum SemanticError {
+       /// The current [`std::time::SystemTime`] is past the offer or invoice's expiration.
+       AlreadyExpired,
+       /// The provided chain hash does not correspond to a supported chain.
+       UnsupportedChain,
        /// An amount was expected but was missing.
        MissingAmount,
        /// The amount exceeded the total bitcoin supply.
        InvalidAmount,
+       /// An amount was provided but was not sufficient in value.
+       InsufficientAmount,
        /// A currency was provided that is not supported.
        UnsupportedCurrency,
+       /// A feature was required but is unknown.
+       UnknownRequiredFeatures,
        /// A required description was not provided.
        MissingDescription,
        /// A signing pubkey was not provided.
        MissingSigningPubkey,
+       /// A quantity was expected but was missing.
+       MissingQuantity,
        /// An unsupported quantity was provided.
        InvalidQuantity,
+       /// A quantity or quantity bounds was provided but was not expected.
+       UnexpectedQuantity,
+       /// Payer metadata was expected but was missing.
+       MissingPayerMetadata,
+       /// A payer id was expected but was missing.
+       MissingPayerId,
 }
 
 impl From<bech32::Error> for ParseError {
@@ -151,3 +170,9 @@ impl From<SemanticError> for ParseError {
                Self::InvalidSemantics(error)
        }
 }
+
+impl From<secp256k1::Error> for ParseError {
+       fn from(error: secp256k1::Error) -> Self {
+               Self::InvalidSignature(error)
+       }
+}
diff --git a/lightning/src/offers/payer.rs b/lightning/src/offers/payer.rs
new file mode 100644 (file)
index 0000000..e389a8f
--- /dev/null
@@ -0,0 +1,25 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Data structures and encoding for `invoice_request_metadata` records.
+
+use crate::util::ser::WithoutLength;
+
+use crate::prelude::*;
+
+/// An unpredictable sequence of bytes typically containing information needed to derive
+/// [`InvoiceRequest::payer_id`].
+///
+/// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
+#[derive(Clone, Debug)]
+pub(super) struct PayerContents(pub Vec<u8>);
+
+tlv_stream!(PayerTlvStream, PayerTlvStreamRef, 0..1, {
+       (0, metadata: (Vec<u8>, WithoutLength)),
+});
index 2c7e5413dafff9d9f027eab116d0967faae6ed2e..375174f6f6afa86c556c6804586adfa52020844a 100644 (file)
@@ -1125,6 +1125,7 @@ impl Writeable for Event {
                                        BumpTransactionEvent::ChannelClose { .. } => {}
                                        BumpTransactionEvent::HTLCResolution { .. } => {}
                                }
+                               write_tlv_fields!(writer, {}); // Write a length field for forwards compat
                        }
                        &Event::ChannelReady { ref channel_id, ref user_channel_id, ref counterparty_node_id, ref channel_type } => {
                                29u8.write(writer)?;
index b5d424efa3451b6d230c313b664385c0183445fe..02d4a81b39e4418daf45f6cb6fc6f61180b1785e 100644 (file)
@@ -20,8 +20,9 @@ use core::convert::TryFrom;
 use core::ops::Deref;
 
 use bitcoin::secp256k1::{PublicKey, SecretKey};
-use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE};
-use bitcoin::secp256k1::ecdsa::Signature;
+use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE, SCHNORR_SIGNATURE_SIZE};
+use bitcoin::secp256k1::ecdsa;
+use bitcoin::secp256k1::schnorr;
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::blockdata::script::Script;
 use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
@@ -499,7 +500,7 @@ impl_array!(12); // for OnionV2
 impl_array!(16); // for IPv6
 impl_array!(32); // for channel id & hmac
 impl_array!(PUBLIC_KEY_SIZE); // for PublicKey
-impl_array!(COMPACT_SIGNATURE_SIZE); // for Signature
+impl_array!(64); // for ecdsa::Signature and schnorr::Signature
 impl_array!(1300); // for OnionPacket.hop_data
 
 impl Writeable for [u16; 8] {
@@ -664,7 +665,7 @@ impl Readable for Vec<u8> {
                Ok(ret)
        }
 }
-impl Writeable for Vec<Signature> {
+impl Writeable for Vec<ecdsa::Signature> {
        #[inline]
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                (self.len() as u16).write(w)?;
@@ -675,7 +676,7 @@ impl Writeable for Vec<Signature> {
        }
 }
 
-impl Readable for Vec<Signature> {
+impl Readable for Vec<ecdsa::Signature> {
        #[inline]
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
                let len: u16 = Readable::read(r)?;
@@ -764,20 +765,32 @@ impl Readable for Sha256dHash {
        }
 }
 
-impl Writeable for Signature {
+impl Writeable for ecdsa::Signature {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                self.serialize_compact().write(w)
        }
-       #[inline]
-       fn serialized_length(&self) -> usize {
-               COMPACT_SIGNATURE_SIZE
-       }
 }
 
-impl Readable for Signature {
+impl Readable for ecdsa::Signature {
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
                let buf: [u8; COMPACT_SIGNATURE_SIZE] = Readable::read(r)?;
-               match Signature::from_compact(&buf) {
+               match ecdsa::Signature::from_compact(&buf) {
+                       Ok(sig) => Ok(sig),
+                       Err(_) => return Err(DecodeError::InvalidValue),
+               }
+       }
+}
+
+impl Writeable for schnorr::Signature {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.as_ref().write(w)
+       }
+}
+
+impl Readable for schnorr::Signature {
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let buf: [u8; SCHNORR_SIGNATURE_SIZE] = Readable::read(r)?;
+               match schnorr::Signature::from_slice(&buf) {
                        Ok(sig) => Ok(sig),
                        Err(_) => return Err(DecodeError::InvalidValue),
                }
index caed542c19b6806cef2b20e849993b2f4e13dc9c..73c4015726191f5ae5ec8b95eadcf82101bc3af8 100644 (file)
@@ -504,15 +504,15 @@ macro_rules! tlv_stream {
                $(($type:expr, $field:ident : $fieldty:tt)),* $(,)*
        }) => {
                #[derive(Debug)]
-               pub(crate) struct $name {
+               pub(super) struct $name {
                        $(
-                               $field: Option<tlv_record_type!($fieldty)>,
+                               pub(super) $field: Option<tlv_record_type!($fieldty)>,
                        )*
                }
 
-               pub(crate) struct $nameref<'a> {
+               pub(super) struct $nameref<'a> {
                        $(
-                               pub(crate) $field: Option<tlv_record_ref_type!($fieldty)>,
+                               pub(super) $field: Option<tlv_record_ref_type!($fieldty)>,
                        )*
                }
 
diff --git a/pending_changelog/matt-abandon-restart.txt b/pending_changelog/matt-abandon-restart.txt
new file mode 100644 (file)
index 0000000..07dc073
--- /dev/null
@@ -0,0 +1,4 @@
+## API Updates
+- `ChannelManager::abandon_payment` docs have been updated to note that the
+  payment may return to pending after a restart if no persistence occurs. This
+  is not a change in behavior - ensure your existing code is safe.