+ /// This event is generated when a payment has been successfully forwarded through us and a
+ /// forwarding fee earned.
+ PaymentForwarded {
+ /// The fee, in milli-satoshis, which was earned as a result of the payment.
+ ///
+ /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC
+ /// was pending, the amount the next hop claimed will have been rounded down to the nearest
+ /// whole satoshi. Thus, the fee calculated here may be higher than expected as we still
+ /// claimed the full value in millisatoshis from the source. In this case,
+ /// `claim_from_onchain_tx` will be set.
+ ///
+ /// If the channel which sent us the payment has been force-closed, we will claim the funds
+ /// via an on-chain transaction. In that case we do not yet know the on-chain transaction
+ /// fees which we will spend and will instead set this to `None`. It is possible duplicate
+ /// `PaymentForwarded` events are generated for the same payment iff `fee_earned_msat` is
+ /// `None`.
+ fee_earned_msat: Option<u64>,
+ /// If this is `true`, the forwarded HTLC was claimed by our counterparty via an on-chain
+ /// transaction.
+ claim_from_onchain_tx: bool,
+ },
+ /// Used to indicate that a channel with the given `channel_id` is in the process of closure.
+ ChannelClosed {
+ /// The channel_id of the channel which has been closed. Note that on-chain transactions
+ /// resolving the channel are likely still awaiting confirmation.
+ channel_id: [u8; 32],
+ /// The `user_channel_id` value passed in to [`ChannelManager::create_channel`], or 0 for
+ /// an inbound channel. This will always be zero for objects serialized with LDK versions
+ /// prior to 0.0.102.
+ ///
+ /// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
+ user_channel_id: u64,
+ /// The reason the channel was closed.
+ reason: ClosureReason
+ },
+ /// Used to indicate to the user that they can abandon the funding transaction and recycle the
+ /// inputs for another purpose.
+ DiscardFunding {
+ /// The channel_id of the channel which has been closed.
+ channel_id: [u8; 32],
+ /// The full transaction received from the user
+ transaction: Transaction
+ }
+}
+
+impl Writeable for Event {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ match self {
+ &Event::FundingGenerationReady { .. } => {
+ 0u8.write(writer)?;
+ // We never write out FundingGenerationReady events as, upon disconnection, peers
+ // drop any channels which have not yet exchanged funding_signed.
+ },
+ &Event::PaymentReceived { ref payment_hash, ref amt, ref purpose } => {
+ 1u8.write(writer)?;
+ let mut payment_secret = None;
+ let mut user_payment_id = None;
+ let payment_preimage;
+ match &purpose {
+ PaymentPurpose::InvoicePayment { payment_preimage: preimage, payment_secret: secret, user_payment_id: id } => {
+ payment_secret = Some(secret);
+ payment_preimage = *preimage;
+ user_payment_id = Some(id);
+ },
+ PaymentPurpose::SpontaneousPayment(preimage) => {
+ payment_preimage = Some(*preimage);
+ }
+ }
+ write_tlv_fields!(writer, {
+ (0, payment_hash, required),
+ (2, payment_secret, option),
+ (4, amt, required),
+ (6, user_payment_id, option),
+ (8, payment_preimage, option),
+ });
+ },
+ &Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
+ 2u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, payment_preimage, required),
+ (1, payment_hash, required),
+ (3, payment_id, option),
+ (5, fee_paid_msat, option),
+ });
+ },
+ &Event::PaymentPathFailed {
+ ref payment_id, ref payment_hash, ref rejected_by_dest, ref network_update,
+ ref all_paths_failed, ref path, ref short_channel_id, ref retry,
+ #[cfg(test)]
+ ref error_code,
+ #[cfg(test)]
+ ref error_data,
+ } => {
+ 3u8.write(writer)?;
+ #[cfg(test)]
+ error_code.write(writer)?;
+ #[cfg(test)]
+ error_data.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, payment_hash, required),
+ (1, network_update, option),
+ (2, rejected_by_dest, required),
+ (3, all_paths_failed, required),
+ (5, path, vec_type),
+ (7, short_channel_id, option),
+ (9, retry, option),
+ (11, payment_id, option),
+ });
+ },
+ &Event::PendingHTLCsForwardable { time_forwardable: _ } => {
+ 4u8.write(writer)?;
+ // Note that we now ignore these on the read end as we'll re-generate them in
+ // ChannelManager, we write them here only for backwards compatibility.
+ },
+ &Event::SpendableOutputs { ref outputs } => {
+ 5u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, VecWriteWrapper(outputs), required),
+ });
+ },
+ &Event::PaymentForwarded { fee_earned_msat, claim_from_onchain_tx } => {
+ 7u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, fee_earned_msat, option),
+ (2, claim_from_onchain_tx, required),
+ });
+ },
+ &Event::ChannelClosed { ref channel_id, ref user_channel_id, ref reason } => {
+ 9u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, channel_id, required),
+ (1, user_channel_id, required),
+ (2, reason, required)
+ });
+ },
+ &Event::DiscardFunding { ref channel_id, ref transaction } => {
+ 11u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, channel_id, required),
+ (2, transaction, required)
+ })
+ },
+ // Note that, going forward, all new events must only write data inside of
+ // `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
+ // data via `write_tlv_fields`.
+ }
+ Ok(())
+ }
+}
+impl MaybeReadable for Event {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, msgs::DecodeError> {
+ match Readable::read(reader)? {
+ // Note that we do not write a length-prefixed TLV for FundingGenerationReady events,
+ // unlike all other events, thus we return immediately here.
+ 0u8 => Ok(None),
+ 1u8 => {
+ let f = || {
+ let mut payment_hash = PaymentHash([0; 32]);
+ let mut payment_preimage = None;
+ let mut payment_secret = None;
+ let mut amt = 0;
+ let mut user_payment_id = None;
+ read_tlv_fields!(reader, {
+ (0, payment_hash, required),
+ (2, payment_secret, option),
+ (4, amt, required),
+ (6, user_payment_id, option),
+ (8, payment_preimage, option),
+ });
+ let purpose = match payment_secret {
+ Some(secret) => PaymentPurpose::InvoicePayment {
+ payment_preimage,
+ payment_secret: secret,
+ user_payment_id: if let Some(id) = user_payment_id {
+ id
+ } else { return Err(msgs::DecodeError::InvalidValue) }
+ },
+ None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
+ None => return Err(msgs::DecodeError::InvalidValue),
+ };
+ Ok(Some(Event::PaymentReceived {
+ payment_hash,
+ amt,
+ purpose,
+ }))
+ };
+ f()
+ },
+ 2u8 => {
+ let f = || {
+ let mut payment_preimage = PaymentPreimage([0; 32]);
+ let mut payment_hash = None;
+ let mut payment_id = None;
+ let mut fee_paid_msat = None;
+ read_tlv_fields!(reader, {
+ (0, payment_preimage, required),
+ (1, payment_hash, option),
+ (3, payment_id, option),
+ (5, fee_paid_msat, option),
+ });
+ if payment_hash.is_none() {
+ payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()));
+ }
+ Ok(Some(Event::PaymentSent {
+ payment_id,
+ payment_preimage,
+ payment_hash: payment_hash.unwrap(),
+ fee_paid_msat,
+ }))
+ };
+ f()
+ },
+ 3u8 => {
+ let f = || {
+ #[cfg(test)]
+ let error_code = Readable::read(reader)?;
+ #[cfg(test)]
+ let error_data = Readable::read(reader)?;
+ let mut payment_hash = PaymentHash([0; 32]);
+ let mut rejected_by_dest = false;
+ let mut network_update = None;
+ let mut all_paths_failed = Some(true);
+ let mut path: Option<Vec<RouteHop>> = Some(vec![]);
+ let mut short_channel_id = None;
+ let mut retry = None;
+ let mut payment_id = None;
+ read_tlv_fields!(reader, {
+ (0, payment_hash, required),
+ (1, network_update, ignorable),
+ (2, rejected_by_dest, required),
+ (3, all_paths_failed, option),
+ (5, path, vec_type),
+ (7, short_channel_id, option),
+ (9, retry, option),
+ (11, payment_id, option),
+ });
+ Ok(Some(Event::PaymentPathFailed {
+ payment_id,
+ payment_hash,
+ rejected_by_dest,
+ network_update,
+ all_paths_failed: all_paths_failed.unwrap(),
+ path: path.unwrap(),
+ short_channel_id,
+ retry,
+ #[cfg(test)]
+ error_code,
+ #[cfg(test)]
+ error_data,
+ }))
+ };
+ f()
+ },
+ 4u8 => Ok(None),
+ 5u8 => {
+ let f = || {
+ let mut outputs = VecReadWrapper(Vec::new());
+ read_tlv_fields!(reader, {
+ (0, outputs, required),
+ });
+ Ok(Some(Event::SpendableOutputs { outputs: outputs.0 }))
+ };
+ f()
+ },
+ 7u8 => {
+ let f = || {
+ let mut fee_earned_msat = None;
+ let mut claim_from_onchain_tx = false;
+ read_tlv_fields!(reader, {
+ (0, fee_earned_msat, option),
+ (2, claim_from_onchain_tx, required),
+ });
+ Ok(Some(Event::PaymentForwarded { fee_earned_msat, claim_from_onchain_tx }))
+ };
+ f()
+ },
+ 9u8 => {
+ let f = || {
+ let mut channel_id = [0; 32];
+ let mut reason = None;
+ let mut user_channel_id_opt = None;
+ read_tlv_fields!(reader, {
+ (0, channel_id, required),
+ (1, user_channel_id_opt, option),
+ (2, reason, ignorable),
+ });
+ if reason.is_none() { return Ok(None); }
+ let user_channel_id = if let Some(id) = user_channel_id_opt { id } else { 0 };
+ Ok(Some(Event::ChannelClosed { channel_id, user_channel_id, reason: reason.unwrap() }))
+ };
+ f()
+ },
+ 11u8 => {
+ let f = || {
+ let mut channel_id = [0; 32];
+ let mut transaction = Transaction{ version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() };
+ read_tlv_fields!(reader, {
+ (0, channel_id, required),
+ (2, transaction, required),
+ });
+ Ok(Some(Event::DiscardFunding { channel_id, transaction } ))
+ };
+ f()
+ },
+ // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
+ // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
+ // reads.
+ x if x % 2 == 1 => {
+ // If the event is of unknown type, assume it was written with `write_tlv_fields`,
+ // which prefixes the whole thing with a length BigSize. Because the event is
+ // odd-type unknown, we should treat it as `Ok(None)` even if it has some TLV
+ // fields that are even. Thus, we avoid using `read_tlv_fields` and simply read
+ // exactly the number of bytes specified, ignoring them entirely.
+ let tlv_len: BigSize = Readable::read(reader)?;
+ FixedLengthReader::new(reader, tlv_len.0)
+ .eat_remaining().map_err(|_| msgs::DecodeError::ShortRead)?;
+ Ok(None)
+ },
+ _ => Err(msgs::DecodeError::InvalidValue)
+ }
+ }