pub mod bump_transaction;
pub use bump_transaction::BumpTransactionEvent;
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
use crate::chain::transaction;
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
use crate::ln::channelmanager::{InterceptId, PaymentId};
use crate::ln::funding::FundingContribution;
use crate::ln::msgs;
use crate::ln::onion_utils::LocalHTLCFailureReason;
use crate::ln::outbound_payment::RecipientOnionFields;
use crate::ln::types::ChannelId;
use crate::offers::invoice::Bolt12Invoice;
use crate::offers::invoice_request::InvoiceRequest;
pub use crate::offers::payer_proof::PaidBolt12Invoice;
use crate::offers::static_invoice::StaticInvoice;
use crate::onion_message::messenger::Responder;
use crate::routing::gossip::NetworkUpdate;
use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters};
use crate::sign::SpendableOutputDescriptor;
use crate::types::features::ChannelTypeFeatures;
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
use crate::types::string::UntrustedString;
use crate::util::errors::APIError;
use crate::util::ser::{
BigSize, FixedLengthReader, MaybeReadable, Readable, ReadableArgs, RequiredWrapper,
UpgradableRequired, WithoutLength, Writeable, Writer,
};
use crate::io;
use crate::sync::Arc;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
use bitcoin::script::ScriptBuf;
use bitcoin::secp256k1::PublicKey;
use bitcoin::{OutPoint, Transaction};
use core::ops::Deref;
#[allow(unused_imports)]
use crate::prelude::*;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum FundingInfo {
Tx {
transaction: Transaction,
},
OutPoint {
outpoint: transaction::OutPoint,
},
Contribution {
inputs: Vec<OutPoint>,
outputs: Vec<ScriptBuf>,
},
}
impl_writeable_tlv_based_enum!(FundingInfo,
(0, Tx) => {
(0, transaction, required)
},
(1, OutPoint) => {
(1, outpoint, required)
},
(2, Contribution) => {
(1, inputs, optional_vec),
(3, outputs, optional_vec),
}
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NegotiationFailureReason {
Unknown,
PeerDisconnected,
CounterpartyAborted {
msg: UntrustedString,
},
NegotiationError {
msg: String,
},
ContributionInvalid,
LocallyCanceled,
ChannelClosing,
FeeRateTooLow,
CannotInitiateRbf,
}
impl NegotiationFailureReason {
pub fn is_retriable(&self) -> bool {
match self {
Self::Unknown
| Self::PeerDisconnected
| Self::ContributionInvalid
| Self::FeeRateTooLow => true,
Self::CounterpartyAborted { .. }
| Self::NegotiationError { .. }
| Self::LocallyCanceled
| Self::ChannelClosing
| Self::CannotInitiateRbf => false,
}
}
}
impl core::fmt::Display for NegotiationFailureReason {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unknown => f.write_str("unknown reason"),
Self::PeerDisconnected => f.write_str("peer disconnected during negotiation"),
Self::CounterpartyAborted { msg } => {
write!(f, "counterparty aborted: {}", msg)
},
Self::NegotiationError { msg } => write!(f, "negotiation error: {}", msg),
Self::ContributionInvalid => f.write_str("funding contribution was invalid"),
Self::LocallyCanceled => f.write_str("splice locally canceled"),
Self::ChannelClosing => f.write_str("channel is closing"),
Self::FeeRateTooLow => f.write_str("feerate too low for RBF"),
Self::CannotInitiateRbf => f.write_str("cannot initiate RBF"),
}
}
}
impl_writeable_tlv_based_enum_upgradable!(NegotiationFailureReason,
(1, Unknown) => {},
(3, PeerDisconnected) => {},
(5, CounterpartyAborted) => {
(1, msg, required),
},
(7, NegotiationError) => {
(1, msg, required),
},
(9, ContributionInvalid) => {},
(11, LocallyCanceled) => {},
(13, ChannelClosing) => {},
(15, FeeRateTooLow) => {},
(17, CannotInitiateRbf) => {},
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PaymentPurpose {
Bolt11InvoicePayment {
payment_preimage: Option<PaymentPreimage>,
payment_secret: PaymentSecret,
},
Bolt12OfferPayment {
payment_preimage: Option<PaymentPreimage>,
payment_secret: PaymentSecret,
payment_context: Bolt12OfferContext,
},
Bolt12RefundPayment {
payment_preimage: Option<PaymentPreimage>,
payment_secret: PaymentSecret,
payment_context: Bolt12RefundContext,
},
SpontaneousPayment(PaymentPreimage),
}
impl PaymentPurpose {
pub fn preimage(&self) -> Option<PaymentPreimage> {
match self {
PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => *payment_preimage,
PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => *payment_preimage,
PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => *payment_preimage,
PaymentPurpose::SpontaneousPayment(preimage) => Some(*preimage),
}
}
pub(crate) fn is_keysend(&self) -> bool {
match self {
PaymentPurpose::Bolt11InvoicePayment { .. } => false,
PaymentPurpose::Bolt12OfferPayment { .. } => false,
PaymentPurpose::Bolt12RefundPayment { .. } => false,
PaymentPurpose::SpontaneousPayment(..) => true,
}
}
pub(crate) fn from_parts(
payment_preimage: Option<PaymentPreimage>, payment_secret: PaymentSecret,
payment_context: Option<PaymentContext>,
) -> Result<Self, ()> {
match payment_context {
None => Ok(PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret }),
Some(PaymentContext::Bolt12Offer(context)) => Ok(PaymentPurpose::Bolt12OfferPayment {
payment_preimage,
payment_secret,
payment_context: context,
}),
Some(PaymentContext::Bolt12Refund(context)) => {
Ok(PaymentPurpose::Bolt12RefundPayment {
payment_preimage,
payment_secret,
payment_context: context,
})
},
Some(PaymentContext::AsyncBolt12Offer(_)) => {
debug_assert!(false);
Err(())
},
}
}
}
impl_writeable_tlv_based_enum_legacy!(PaymentPurpose,
(0, Bolt11InvoicePayment) => {
(0, payment_preimage, option),
(2, payment_secret, required),
},
(4, Bolt12OfferPayment) => {
(0, payment_preimage, option),
(2, payment_secret, required),
(4, payment_context, required),
},
(6, Bolt12RefundPayment) => {
(0, payment_preimage, option),
(2, payment_secret, required),
(4, payment_context, required),
},
;
(2, SpontaneousPayment)
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClaimedHTLC {
pub counterparty_node_id: Option<PublicKey>,
pub channel_id: ChannelId,
pub user_channel_id: u128,
pub cltv_expiry: u32,
pub value_msat: u64,
pub counterparty_skimmed_fee_msat: u64,
}
impl_writeable_tlv_based!(ClaimedHTLC, {
(0, channel_id, required),
(1, counterparty_skimmed_fee_msat, (default_value, 0u64)),
(2, user_channel_id, required),
(3, counterparty_node_id, option),
(4, cltv_expiry, required),
(6, value_msat, required),
});
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PathFailure {
InitialSend {
err: APIError,
},
OnPath {
network_update: Option<NetworkUpdate>,
},
}
impl_writeable_tlv_based_enum_upgradable!(PathFailure,
(0, OnPath) => {
(0, network_update, upgradable_option),
},
(2, InitialSend) => {
(0, err, upgradable_required),
},
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ClosureReason {
CounterpartyForceClosed {
peer_msg: UntrustedString,
},
HolderForceClosed {
broadcasted_latest_txn: Option<bool>,
message: String,
},
LegacyCooperativeClosure,
CounterpartyInitiatedCooperativeClosure,
LocallyInitiatedCooperativeClosure,
CommitmentTxConfirmed,
FundingTimedOut,
ProcessingError {
err: String,
},
DisconnectedPeer,
OutdatedChannelManager,
CounterpartyCoopClosedUnfundedChannel,
LocallyCoopClosedUnfundedChannel,
FundingBatchClosure,
HTLCsTimedOut {
payment_hash: Option<PaymentHash>,
},
PeerFeerateTooLow {
peer_feerate_sat_per_kw: u32,
required_feerate_sat_per_kw: u32,
},
}
impl core::fmt::Display for ClosureReason {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.write_str("Channel closed because ")?;
match self {
ClosureReason::CounterpartyForceClosed { peer_msg } => {
f.write_fmt(format_args!("counterparty force-closed with message: {}", peer_msg))
},
ClosureReason::HolderForceClosed { broadcasted_latest_txn, message } => {
f.write_str("user force-closed the channel with the message \"")?;
f.write_str(message)?;
if let Some(brodcasted) = broadcasted_latest_txn {
write!(
f,
"\" and {} the latest transaction",
if *brodcasted { "broadcasted" } else { "elected not to broadcast" }
)
} else {
Ok(())
}
},
ClosureReason::LegacyCooperativeClosure => {
f.write_str("the channel was cooperatively closed")
},
ClosureReason::CounterpartyInitiatedCooperativeClosure => {
f.write_str("the channel was cooperatively closed by our peer")
},
ClosureReason::LocallyInitiatedCooperativeClosure => {
f.write_str("the channel was cooperatively closed by us")
},
ClosureReason::CommitmentTxConfirmed => {
f.write_str("commitment or closing transaction was confirmed on chain.")
},
ClosureReason::FundingTimedOut => write!(
f,
"funding transaction failed to confirm within {} blocks",
FUNDING_CONF_DEADLINE_BLOCKS
),
ClosureReason::ProcessingError { err } => {
f.write_str("of an exception: ")?;
f.write_str(&err)
},
ClosureReason::DisconnectedPeer => {
f.write_str("the peer disconnected prior to the channel being funded")
},
ClosureReason::OutdatedChannelManager => f.write_str(
"the ChannelManager read from disk was stale compared to ChannelMonitor(s)",
),
ClosureReason::CounterpartyCoopClosedUnfundedChannel => {
f.write_str("the peer requested the unfunded channel be closed")
},
ClosureReason::LocallyCoopClosedUnfundedChannel => {
f.write_str("we requested the unfunded channel be closed")
},
ClosureReason::FundingBatchClosure => {
f.write_str("another channel in the same funding batch closed")
},
ClosureReason::HTLCsTimedOut { payment_hash: Some(hash) } => f.write_fmt(format_args!(
"HTLC(s) on the channel timed out (including the HTLC with payment hash {hash})",
)),
ClosureReason::HTLCsTimedOut { payment_hash: None } => {
f.write_fmt(format_args!("HTLC(s) on the channel timed out"))
},
ClosureReason::PeerFeerateTooLow {
peer_feerate_sat_per_kw,
required_feerate_sat_per_kw,
} => f.write_fmt(format_args!(
"peer provided a feerate ({} sat/kw) which was below our lower bound ({} sat/kw)",
peer_feerate_sat_per_kw, required_feerate_sat_per_kw,
)),
}
}
}
impl_writeable_tlv_based_enum_upgradable!(ClosureReason,
(0, CounterpartyForceClosed) => { (1, peer_msg, required) },
(1, FundingTimedOut) => {},
(2, HolderForceClosed) => {
(1, broadcasted_latest_txn, option),
(3, message, (default_value, String::new())),
},
(6, CommitmentTxConfirmed) => {},
(4, LegacyCooperativeClosure) => {},
(8, ProcessingError) => { (1, err, required) },
(10, DisconnectedPeer) => {},
(12, OutdatedChannelManager) => {},
(13, CounterpartyCoopClosedUnfundedChannel) => {},
(15, FundingBatchClosure) => {},
(17, CounterpartyInitiatedCooperativeClosure) => {},
(19, LocallyInitiatedCooperativeClosure) => {},
(21, HTLCsTimedOut) => {
(1, payment_hash, option),
},
(23, PeerFeerateTooLow) => {
(0, peer_feerate_sat_per_kw, required),
(2, required_feerate_sat_per_kw, required),
},
(25, LocallyCoopClosedUnfundedChannel) => {},
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HTLCHandlingFailureType {
Forward {
node_id: Option<PublicKey>,
channel_id: ChannelId,
},
UnknownNextHop {
requested_forward_scid: u64,
},
InvalidForward {
requested_forward_scid: u64,
},
InvalidOnion,
Receive {
payment_hash: PaymentHash,
},
TrampolineForward {},
}
impl_writeable_tlv_based_enum_upgradable!(HTLCHandlingFailureType,
(0, Forward) => {
(0, node_id, required),
(2, channel_id, required),
},
(1, InvalidForward) => {
(0, requested_forward_scid, required),
},
(2, UnknownNextHop) => {
(0, requested_forward_scid, required),
},
(3, InvalidOnion) => {},
(4, Receive) => {
(0, payment_hash, required),
},
(5, TrampolineForward) => {},
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HTLCHandlingFailureReason {
Downstream,
Local {
reason: LocalHTLCFailureReason,
},
}
impl_writeable_tlv_based_enum!(HTLCHandlingFailureReason,
(1, Downstream) => {},
(3, Local) => {
(0, reason, required),
},
);
impl From<LocalHTLCFailureReason> for HTLCHandlingFailureReason {
fn from(value: LocalHTLCFailureReason) -> Self {
HTLCHandlingFailureReason::Local { reason: value }
}
}
enum InterceptNextHop {
FakeScid { requested_next_hop_scid: u64 },
}
impl_writeable_tlv_based_enum!(InterceptNextHop,
(0, FakeScid) => {
(0, requested_next_hop_scid, required),
},
);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PaymentFailureReason {
RecipientRejected,
UserAbandoned,
#[cfg_attr(
feature = "std",
doc = "We exhausted all of our retry attempts while trying to send the payment, or we"
)]
#[cfg_attr(feature = "std", doc = "exhausted the [`Retry::Timeout`] if the user set one.")]
#[cfg_attr(
not(feature = "std"),
doc = "We exhausted all of our retry attempts while trying to send the payment."
)]
#[cfg_attr(feature = "std", doc = "")]
#[cfg_attr(
feature = "std",
doc = "[`Retry::Timeout`]: crate::ln::outbound_payment::Retry::Timeout"
)]
RetriesExhausted,
PaymentExpired,
RouteNotFound,
UnexpectedError,
UnknownRequiredFeatures,
InvoiceRequestExpired,
InvoiceRequestRejected,
BlindedPathCreationFailed,
}
impl_writeable_tlv_based_enum_upgradable!(PaymentFailureReason,
(0, RecipientRejected) => {},
(1, UnknownRequiredFeatures) => {},
(2, UserAbandoned) => {},
(3, InvoiceRequestExpired) => {},
(4, RetriesExhausted) => {},
(5, InvoiceRequestRejected) => {},
(6, PaymentExpired) => {},
(7, BlindedPathCreationFailed) => {},
(8, RouteNotFound) => {},
(10, UnexpectedError) => {},
);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InboundChannelFunds {
PushMsat(u64),
DualFunded,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HTLCLocator {
pub channel_id: ChannelId,
pub amount_msat: Option<u64>,
pub user_channel_id: Option<u128>,
pub node_id: Option<PublicKey>,
}
impl_writeable_tlv_based!(HTLCLocator, {
(1, channel_id, required),
(3, user_channel_id, option),
(5, node_id, option),
(7, amount_msat, option),
});
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
FundingGenerationReady {
temporary_channel_id: ChannelId,
counterparty_node_id: PublicKey,
channel_value_satoshis: u64,
output_script: ScriptBuf,
user_channel_id: u128,
},
FundingTxBroadcastSafe {
channel_id: ChannelId,
user_channel_id: u128,
funding_txo: OutPoint,
counterparty_node_id: PublicKey,
former_temporary_channel_id: ChannelId,
},
PaymentClaimable {
receiver_node_id: Option<PublicKey>,
payment_hash: PaymentHash,
onion_fields: Option<RecipientOnionFields>,
amount_msat: u64,
counterparty_skimmed_fee_msat: u64,
purpose: PaymentPurpose,
receiving_channel_ids: Vec<(ChannelId, Option<u128>)>,
claim_deadline: Option<u32>,
payment_id: Option<PaymentId>,
},
PaymentClaimed {
receiver_node_id: Option<PublicKey>,
payment_hash: PaymentHash,
amount_msat: u64,
purpose: PaymentPurpose,
htlcs: Vec<ClaimedHTLC>,
sender_intended_total_msat: Option<u64>,
onion_fields: Option<RecipientOnionFields>,
payment_id: Option<PaymentId>,
},
ConnectionNeeded {
node_id: PublicKey,
addresses: Vec<msgs::SocketAddress>,
},
InvoiceReceived {
payment_id: PaymentId,
invoice: Bolt12Invoice,
context: Option<OffersContext>,
responder: Option<Responder>,
},
PaymentSent {
payment_id: Option<PaymentId>,
payment_preimage: PaymentPreimage,
payment_hash: PaymentHash,
amount_msat: Option<u64>,
fee_paid_msat: Option<u64>,
bolt12_invoice: Option<PaidBolt12Invoice>,
},
PaymentFailed {
payment_id: PaymentId,
payment_hash: Option<PaymentHash>,
reason: Option<PaymentFailureReason>,
},
PaymentPathSuccessful {
payment_id: PaymentId,
payment_hash: Option<PaymentHash>,
path: Path,
hold_times: Vec<u32>,
},
PaymentPathFailed {
payment_id: Option<PaymentId>,
payment_hash: PaymentHash,
payment_failed_permanently: bool,
failure: PathFailure,
path: Path,
short_channel_id: Option<u64>,
#[cfg(any(test, feature = "_test_utils"))]
error_code: Option<u16>,
#[cfg(any(test, feature = "_test_utils"))]
error_data: Option<Vec<u8>>,
hold_times: Vec<u32>,
},
ProbeSuccessful {
payment_id: PaymentId,
payment_hash: PaymentHash,
path: Path,
},
ProbeFailed {
payment_id: PaymentId,
payment_hash: PaymentHash,
path: Path,
short_channel_id: Option<u64>,
},
HTLCIntercepted {
intercept_id: InterceptId,
requested_next_hop_scid: u64,
payment_hash: PaymentHash,
inbound_amount_msat: u64,
expected_outbound_amount_msat: u64,
outgoing_htlc_expiry_block_height: Option<u32>,
},
SpendableOutputs {
outputs: Vec<SpendableOutputDescriptor>,
channel_id: Option<ChannelId>,
counterparty_node_id: Option<PublicKey>,
},
PaymentForwarded {
prev_htlcs: Vec<HTLCLocator>,
next_htlcs: Vec<HTLCLocator>,
total_fee_earned_msat: Option<u64>,
skimmed_fee_msat: Option<u64>,
claim_from_onchain_tx: bool,
outbound_amount_forwarded_msat: u64,
},
ChannelPending {
channel_id: ChannelId,
user_channel_id: u128,
former_temporary_channel_id: Option<ChannelId>,
counterparty_node_id: PublicKey,
funding_txo: OutPoint,
channel_type: Option<ChannelTypeFeatures>,
funding_redeem_script: Option<ScriptBuf>,
},
ChannelReady {
channel_id: ChannelId,
user_channel_id: u128,
counterparty_node_id: PublicKey,
funding_txo: Option<OutPoint>,
channel_type: ChannelTypeFeatures,
},
ChannelClosed {
channel_id: ChannelId,
user_channel_id: u128,
reason: ClosureReason,
counterparty_node_id: Option<PublicKey>,
channel_capacity_sats: Option<u64>,
channel_funding_txo: Option<transaction::OutPoint>,
last_local_balance_msat: Option<u64>,
},
SpliceNegotiated {
channel_id: ChannelId,
user_channel_id: u128,
counterparty_node_id: PublicKey,
new_funding_txo: OutPoint,
channel_type: ChannelTypeFeatures,
new_funding_redeem_script: ScriptBuf,
},
SpliceNegotiationFailed {
channel_id: ChannelId,
user_channel_id: u128,
counterparty_node_id: PublicKey,
reason: NegotiationFailureReason,
contribution: Option<FundingContribution>,
},
DiscardFunding {
channel_id: ChannelId,
funding_info: FundingInfo,
},
OpenChannelRequest {
temporary_channel_id: ChannelId,
counterparty_node_id: PublicKey,
funding_satoshis: u64,
channel_negotiation_type: InboundChannelFunds,
channel_type: ChannelTypeFeatures,
is_announced: bool,
params: msgs::ChannelParameters,
},
HTLCHandlingFailed {
prev_channel_ids: Vec<ChannelId>,
failure_type: HTLCHandlingFailureType,
failure_reason: Option<HTLCHandlingFailureReason>,
},
BumpTransaction(BumpTransactionEvent),
OnionMessageIntercepted {
prev_hop: Option<PublicKey>,
next_hop: NextMessageHop,
message: msgs::OnionMessage,
},
OnionMessagePeerConnected {
peer_node_id: PublicKey,
},
PersistStaticInvoice {
invoice: StaticInvoice,
invoice_request_path: BlindedMessagePath,
invoice_slot: u16,
recipient_id: Vec<u8>,
invoice_persisted_path: Responder,
},
StaticInvoiceRequested {
recipient_id: Vec<u8>,
invoice_slot: u16,
reply_path: Responder,
invoice_request: InvoiceRequest,
},
FundingTransactionReadyForSigning {
channel_id: ChannelId,
counterparty_node_id: PublicKey,
user_channel_id: u128,
unsigned_transaction: Transaction,
},
}
impl Writeable for Event {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
match self {
&Event::FundingGenerationReady { .. } => {
0u8.write(writer)?;
},
&Event::PaymentClaimable {
ref payment_hash,
ref amount_msat,
counterparty_skimmed_fee_msat,
ref purpose,
ref receiver_node_id,
ref receiving_channel_ids,
ref claim_deadline,
ref onion_fields,
ref payment_id,
} => {
1u8.write(writer)?;
let mut payment_secret = None;
let payment_preimage;
let mut payment_context = None;
match &purpose {
PaymentPurpose::Bolt11InvoicePayment {
payment_preimage: preimage,
payment_secret: secret,
} => {
payment_secret = Some(secret);
payment_preimage = *preimage;
},
PaymentPurpose::Bolt12OfferPayment {
payment_preimage: preimage,
payment_secret: secret,
payment_context: context,
} => {
payment_secret = Some(secret);
payment_preimage = *preimage;
payment_context = Some(PaymentContextRef::Bolt12Offer(context));
},
PaymentPurpose::Bolt12RefundPayment {
payment_preimage: preimage,
payment_secret: secret,
payment_context: context,
} => {
payment_secret = Some(secret);
payment_preimage = *preimage;
payment_context = Some(PaymentContextRef::Bolt12Refund(context));
},
PaymentPurpose::SpontaneousPayment(preimage) => {
payment_preimage = Some(*preimage);
},
}
let skimmed_fee_opt = if counterparty_skimmed_fee_msat == 0 {
None
} else {
Some(counterparty_skimmed_fee_msat)
};
let (receiving_channel_id_legacy, receiving_user_channel_id_legacy) =
match receiving_channel_ids.last() {
Some((chan_id, user_chan_id)) => (Some(*chan_id), *user_chan_id),
None => (None, None),
};
write_tlv_fields!(writer, {
(0, payment_hash, required),
(1, receiver_node_id, option),
(2, payment_secret, option),
(3, receiving_channel_id_legacy, option),
(4, amount_msat, required),
(5, receiving_user_channel_id_legacy, option),
(7, claim_deadline, option),
(8, payment_preimage, option),
(9, onion_fields, option),
(10, skimmed_fee_opt, option),
(11, payment_context, option),
(13, payment_id, option),
(15, *receiving_channel_ids, optional_vec),
});
},
&Event::PaymentSent {
ref payment_id,
ref payment_preimage,
ref payment_hash,
ref amount_msat,
ref fee_paid_msat,
ref bolt12_invoice,
} => {
2u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_preimage, required),
(1, payment_hash, required),
(3, payment_id, option),
(5, fee_paid_msat, option),
(7, amount_msat, option),
(9, bolt12_invoice, option),
});
},
&Event::PaymentPathFailed {
ref payment_id,
ref payment_hash,
ref payment_failed_permanently,
ref failure,
ref path,
ref short_channel_id,
#[cfg(any(test, feature = "_test_utils"))]
ref error_code,
#[cfg(any(test, feature = "_test_utils"))]
ref error_data,
ref hold_times,
} => {
3u8.write(writer)?;
#[cfg(any(test, feature = "_test_utils"))]
error_code.write(writer)?;
#[cfg(any(test, feature = "_test_utils"))]
error_data.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_hash, required),
(1, None::<NetworkUpdate>, option), (2, payment_failed_permanently, required),
(3, false, required), (4, path.blinded_tail, option),
(5, path.hops, required_vec),
(7, short_channel_id, option),
(9, None::<RouteParameters>, option), (11, payment_id, option),
(13, failure, required),
(15, *hold_times, optional_vec),
});
},
&Event::SpendableOutputs { ref outputs, channel_id, counterparty_node_id } => {
5u8.write(writer)?;
write_tlv_fields!(writer, {
(0, WithoutLength(outputs), required),
(1, channel_id, option),
(3, counterparty_node_id, option),
});
},
&Event::HTLCIntercepted {
requested_next_hop_scid,
payment_hash,
inbound_amount_msat,
expected_outbound_amount_msat,
intercept_id,
outgoing_htlc_expiry_block_height,
} => {
6u8.write(writer)?;
let intercept_scid = InterceptNextHop::FakeScid { requested_next_hop_scid };
write_tlv_fields!(writer, {
(0, intercept_id, required),
(1, outgoing_htlc_expiry_block_height, option),
(2, intercept_scid, required),
(4, payment_hash, required),
(6, inbound_amount_msat, required),
(8, expected_outbound_amount_msat, required),
});
},
&Event::PaymentForwarded {
ref prev_htlcs,
ref next_htlcs,
total_fee_earned_msat,
skimmed_fee_msat,
claim_from_onchain_tx,
outbound_amount_forwarded_msat,
} => {
7u8.write(writer)?;
debug_assert!(
!prev_htlcs.is_empty(),
"at least one prev_htlc required for PaymentForwarded",
);
debug_assert!(
!next_htlcs.is_empty(),
"at least one next_htlc required for PaymentForwarded",
);
let empty_locator = HTLCLocator {
channel_id: ChannelId::new_zero(),
amount_msat: None,
user_channel_id: None,
node_id: None,
};
let legacy_prev = prev_htlcs.first().unwrap_or(&empty_locator);
let legacy_next = next_htlcs.first().unwrap_or(&empty_locator);
write_tlv_fields!(writer, {
(0, total_fee_earned_msat, option),
(1, Some(legacy_prev.channel_id), option),
(2, claim_from_onchain_tx, required),
(3, Some(legacy_next.channel_id), option),
(5, outbound_amount_forwarded_msat, required),
(7, skimmed_fee_msat, option),
(9, legacy_prev.user_channel_id, option),
(11, legacy_next.user_channel_id, option),
(13, legacy_prev.node_id, option),
(15, legacy_next.node_id, option),
(17, *prev_htlcs, required),
(19, *next_htlcs, required),
});
},
&Event::ChannelClosed {
ref channel_id,
ref user_channel_id,
ref reason,
ref counterparty_node_id,
ref channel_capacity_sats,
ref channel_funding_txo,
ref last_local_balance_msat,
} => {
9u8.write(writer)?;
let user_channel_id_low = *user_channel_id as u64;
let user_channel_id_high = (*user_channel_id >> 64) as u64;
write_tlv_fields!(writer, {
(0, channel_id, required),
(1, user_channel_id_low, required),
(2, reason, required),
(3, user_channel_id_high, required),
(5, counterparty_node_id, option),
(7, channel_capacity_sats, option),
(9, channel_funding_txo, option),
(11, last_local_balance_msat, option)
});
},
&Event::DiscardFunding { ref channel_id, ref funding_info } => {
11u8.write(writer)?;
let transaction = if let FundingInfo::Tx { transaction } = funding_info {
Some(transaction)
} else {
None
};
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, transaction, option),
(4, funding_info, required),
})
},
&Event::PaymentPathSuccessful {
ref payment_id,
ref payment_hash,
ref path,
ref hold_times,
} => {
13u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_id, required),
(1, *hold_times, optional_vec),
(2, payment_hash, option),
(4, path.hops, required_vec),
(6, path.blinded_tail, option),
})
},
&Event::PaymentFailed { ref payment_id, ref payment_hash, ref reason } => {
15u8.write(writer)?;
let (payment_hash, invoice_received) = match payment_hash {
Some(payment_hash) => (payment_hash, true),
None => (&PaymentHash([0; 32]), false),
};
let legacy_reason = match reason {
None => &None,
Some(PaymentFailureReason::RecipientRejected)
| Some(PaymentFailureReason::UserAbandoned)
| Some(PaymentFailureReason::RetriesExhausted)
| Some(PaymentFailureReason::PaymentExpired)
| Some(PaymentFailureReason::RouteNotFound)
| Some(PaymentFailureReason::UnexpectedError) => reason,
Some(PaymentFailureReason::UnknownRequiredFeatures) => {
&Some(PaymentFailureReason::RecipientRejected)
},
Some(PaymentFailureReason::InvoiceRequestExpired) => {
&Some(PaymentFailureReason::RetriesExhausted)
},
Some(PaymentFailureReason::InvoiceRequestRejected) => {
&Some(PaymentFailureReason::RecipientRejected)
},
Some(PaymentFailureReason::BlindedPathCreationFailed) => {
&Some(PaymentFailureReason::RouteNotFound)
},
};
write_tlv_fields!(writer, {
(0, payment_id, required),
(1, legacy_reason, option),
(2, payment_hash, required),
(3, invoice_received, required),
(5, reason, option),
})
},
&Event::OpenChannelRequest { .. } => {
17u8.write(writer)?;
},
&Event::PaymentClaimed {
ref payment_hash,
ref amount_msat,
ref purpose,
ref receiver_node_id,
ref htlcs,
ref sender_intended_total_msat,
ref onion_fields,
ref payment_id,
} => {
19u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_hash, required),
(1, receiver_node_id, option),
(2, purpose, required),
(4, amount_msat, required),
(5, *htlcs, optional_vec),
(7, sender_intended_total_msat, option),
(9, onion_fields, option),
(11, payment_id, option),
});
},
&Event::ProbeSuccessful { ref payment_id, ref payment_hash, ref path } => {
21u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_id, required),
(2, payment_hash, required),
(4, path.hops, required_vec),
(6, path.blinded_tail, option),
})
},
&Event::ProbeFailed {
ref payment_id,
ref payment_hash,
ref path,
ref short_channel_id,
} => {
23u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_id, required),
(2, payment_hash, required),
(4, path.hops, required_vec),
(6, short_channel_id, option),
(8, path.blinded_tail, option),
})
},
&Event::HTLCHandlingFailed {
ref prev_channel_ids,
ref failure_type,
ref failure_reason,
} => {
25u8.write(writer)?;
debug_assert!(
!prev_channel_ids.is_empty(),
"at least one prev_channel_id required for HTLCHandlingFailed"
);
let zero_id = ChannelId::new_zero();
let legacy_chan_id = prev_channel_ids.first().unwrap_or(&zero_id);
write_tlv_fields!(writer, {
(0, legacy_chan_id, required),
(1, failure_reason, option),
(2, failure_type, required),
(3, *prev_channel_ids, required),
})
},
&Event::BumpTransaction(ref event) => {
27u8.write(writer)?;
match event {
BumpTransactionEvent::ChannelClose { .. } => {},
BumpTransactionEvent::HTLCResolution { .. } => {},
}
write_tlv_fields!(writer, {}); },
&Event::ChannelReady {
ref channel_id,
ref user_channel_id,
ref counterparty_node_id,
ref funding_txo,
ref channel_type,
} => {
29u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(1, funding_txo, option),
(2, user_channel_id, required),
(4, counterparty_node_id, required),
(6, channel_type, required),
});
},
&Event::ChannelPending {
ref channel_id,
ref user_channel_id,
ref former_temporary_channel_id,
ref counterparty_node_id,
ref funding_txo,
ref channel_type,
ref funding_redeem_script,
} => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(1, channel_type, option),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
(9, funding_redeem_script, option),
});
},
&Event::ConnectionNeeded { .. } => {
35u8.write(writer)?;
},
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
39u8.write(writer)?;
write_tlv_fields!(writer, {
(0, peer_node_id, required),
});
},
&Event::InvoiceReceived { ref payment_id, ref invoice, ref context, ref responder } => {
41u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_id, required),
(2, invoice, required),
(4, context, option),
(6, responder, option),
});
},
&Event::FundingTxBroadcastSafe {
ref channel_id,
ref user_channel_id,
ref funding_txo,
ref counterparty_node_id,
ref former_temporary_channel_id,
} => {
43u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, funding_txo, required),
(6, counterparty_node_id, required),
(8, former_temporary_channel_id, required),
});
},
&Event::PersistStaticInvoice { .. } => {
45u8.write(writer)?;
},
&Event::StaticInvoiceRequested { .. } => {
47u8.write(writer)?;
},
&Event::FundingTransactionReadyForSigning { .. } => {
49u8.write(writer)?;
},
&Event::SpliceNegotiated {
ref channel_id,
ref user_channel_id,
ref counterparty_node_id,
ref new_funding_txo,
ref channel_type,
ref new_funding_redeem_script,
} => {
50u8.write(writer)?;
write_tlv_fields!(writer, {
(1, channel_id, required),
(3, channel_type, required),
(5, user_channel_id, required),
(7, counterparty_node_id, required),
(9, new_funding_txo, required),
(11, new_funding_redeem_script, required),
});
},
&Event::SpliceNegotiationFailed {
ref channel_id,
ref user_channel_id,
ref counterparty_node_id,
ref reason,
ref contribution,
} => {
52u8.write(writer)?;
write_tlv_fields!(writer, {
(1, channel_id, required),
(5, user_channel_id, required),
(7, counterparty_node_id, required),
(11, reason, required),
(13, contribution, option),
});
},
}
Ok(())
}
}
impl MaybeReadable for Event {
fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, msgs::DecodeError> {
match Readable::read(reader)? {
0u8 => Ok(None),
1u8 => {
let mut f = || {
let mut payment_hash = PaymentHash([0; 32]);
let mut payment_preimage = None;
let mut payment_secret = None;
let mut amount_msat = 0;
let mut counterparty_skimmed_fee_msat_opt = None;
let mut receiver_node_id = None;
let mut _user_payment_id = None::<u64>; let mut receiving_channel_id_legacy = None;
let mut claim_deadline = None;
let mut receiving_user_channel_id_legacy = None;
let mut onion_fields = None;
let mut payment_context = None;
let mut payment_id = None;
let mut receiving_channel_ids_opt = None;
read_tlv_fields!(reader, {
(0, payment_hash, required),
(1, receiver_node_id, option),
(2, payment_secret, option),
(3, receiving_channel_id_legacy, option),
(4, amount_msat, required),
(5, receiving_user_channel_id_legacy, option),
(6, _user_payment_id, option),
(7, claim_deadline, option),
(8, payment_preimage, option),
(9, onion_fields, (option: ReadableArgs, amount_msat)),
(10, counterparty_skimmed_fee_msat_opt, option),
(11, payment_context, option),
(13, payment_id, option),
(15, receiving_channel_ids_opt, optional_vec),
});
let purpose = match payment_secret {
Some(secret) => {
PaymentPurpose::from_parts(payment_preimage, secret, payment_context)
.map_err(|()| msgs::DecodeError::InvalidValue)?
},
None if payment_preimage.is_some() => {
PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap())
},
None => return Err(msgs::DecodeError::InvalidValue),
};
let receiving_channel_ids = receiving_channel_ids_opt
.or_else(|| {
receiving_channel_id_legacy
.map(|chan_id| vec![(chan_id, receiving_user_channel_id_legacy)])
})
.unwrap_or_default();
Ok(Some(Event::PaymentClaimable {
receiver_node_id,
payment_hash,
amount_msat,
counterparty_skimmed_fee_msat: counterparty_skimmed_fee_msat_opt
.unwrap_or(0),
purpose,
receiving_channel_ids,
claim_deadline,
onion_fields,
payment_id,
}))
};
f()
},
2u8 => {
let mut f = || {
let mut payment_preimage = PaymentPreimage([0; 32]);
let mut payment_hash = None;
let mut payment_id = None;
let mut amount_msat = None;
let mut fee_paid_msat = None;
let mut bolt12_invoice = None;
read_tlv_fields!(reader, {
(0, payment_preimage, required),
(1, payment_hash, option),
(3, payment_id, option),
(5, fee_paid_msat, option),
(7, amount_msat, option),
(9, bolt12_invoice, option),
});
if payment_hash.is_none() {
payment_hash = Some(PaymentHash(
Sha256::hash(&payment_preimage.0[..]).to_byte_array(),
));
}
Ok(Some(Event::PaymentSent {
payment_id,
payment_preimage,
payment_hash: payment_hash.unwrap(),
amount_msat,
fee_paid_msat,
bolt12_invoice,
}))
};
f()
},
3u8 => {
let mut f = || {
#[cfg(any(test, feature = "_test_utils"))]
let error_code = Readable::read(reader)?;
#[cfg(any(test, feature = "_test_utils"))]
let error_data = Readable::read(reader)?;
let mut payment_hash = PaymentHash([0; 32]);
let mut payment_failed_permanently = false;
let mut network_update = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut path: Option<Vec<RouteHop>> = Some(vec![]);
let mut short_channel_id = None;
let mut payment_id = None;
let mut failure_opt = None;
let mut hold_times = None;
read_tlv_fields!(reader, {
(0, payment_hash, required),
(1, network_update, upgradable_option),
(2, payment_failed_permanently, required),
(4, blinded_tail, option),
(5, path, optional_vec),
(7, short_channel_id, option),
(11, payment_id, option),
(13, failure_opt, upgradable_option),
(15, hold_times, optional_vec),
});
let hold_times = hold_times.unwrap_or(Vec::new());
let failure =
failure_opt.unwrap_or_else(|| PathFailure::OnPath { network_update });
Ok(Some(Event::PaymentPathFailed {
payment_id,
payment_hash,
payment_failed_permanently,
failure,
path: Path { hops: path.unwrap(), blinded_tail },
short_channel_id,
#[cfg(any(test, feature = "_test_utils"))]
error_code,
#[cfg(any(test, feature = "_test_utils"))]
error_data,
hold_times,
}))
};
f()
},
4u8 => Ok(None),
5u8 => {
let mut f = || {
let mut outputs = WithoutLength(Vec::new());
let mut channel_id: Option<ChannelId> = None;
let mut counterparty_node_id: Option<PublicKey> = None;
read_tlv_fields!(reader, {
(0, outputs, required),
(1, channel_id, option),
(3, counterparty_node_id, option),
});
Ok(Some(Event::SpendableOutputs {
outputs: outputs.0,
channel_id,
counterparty_node_id,
}))
};
f()
},
6u8 => {
let mut payment_hash = PaymentHash([0; 32]);
let mut intercept_id = InterceptId([0; 32]);
let mut requested_next_hop_scid =
InterceptNextHop::FakeScid { requested_next_hop_scid: 0 };
let mut inbound_amount_msat = 0;
let mut expected_outbound_amount_msat = 0;
let mut outgoing_htlc_expiry_block_height = None;
read_tlv_fields!(reader, {
(0, intercept_id, required),
(1, outgoing_htlc_expiry_block_height, option),
(2, requested_next_hop_scid, required),
(4, payment_hash, required),
(6, inbound_amount_msat, required),
(8, expected_outbound_amount_msat, required),
});
let next_scid = match requested_next_hop_scid {
InterceptNextHop::FakeScid { requested_next_hop_scid: scid } => scid,
};
Ok(Some(Event::HTLCIntercepted {
payment_hash,
requested_next_hop_scid: next_scid,
inbound_amount_msat,
expected_outbound_amount_msat,
intercept_id,
outgoing_htlc_expiry_block_height,
}))
},
7u8 => {
let mut f = || {
let mut prev_channel_id_legacy = None;
let mut next_channel_id_legacy = None;
let mut prev_user_channel_id_legacy = None;
let mut next_user_channel_id_legacy = None;
let mut prev_node_id_legacy = None;
let mut next_node_id_legacy = None;
let mut total_fee_earned_msat = None;
let mut skimmed_fee_msat = None;
let mut claim_from_onchain_tx = false;
let mut outbound_amount_forwarded_msat = 0;
let mut prev_htlcs = vec![];
let mut next_htlcs = vec![];
read_tlv_fields!(reader, {
(0, total_fee_earned_msat, option),
(1, prev_channel_id_legacy, option),
(2, claim_from_onchain_tx, required),
(3, next_channel_id_legacy, option),
(5, outbound_amount_forwarded_msat, required),
(7, skimmed_fee_msat, option),
(9, prev_user_channel_id_legacy, option),
(11, next_user_channel_id_legacy, option),
(13, prev_node_id_legacy, option),
(15, next_node_id_legacy, option),
(17, prev_htlcs, (default_value, vec![HTLCLocator{
channel_id: prev_channel_id_legacy.ok_or(DecodeError::InvalidValue)?,
amount_msat: total_fee_earned_msat
.map(|fee| outbound_amount_forwarded_msat + fee),
user_channel_id: prev_user_channel_id_legacy,
node_id: prev_node_id_legacy,
}])),
(19, next_htlcs, (default_value, vec![HTLCLocator{
channel_id: next_channel_id_legacy.ok_or(DecodeError::InvalidValue)?,
amount_msat: Some(outbound_amount_forwarded_msat),
user_channel_id: next_user_channel_id_legacy,
node_id: next_node_id_legacy,
}])),
});
Ok(Some(Event::PaymentForwarded {
prev_htlcs,
next_htlcs,
total_fee_earned_msat,
skimmed_fee_msat,
claim_from_onchain_tx,
outbound_amount_forwarded_msat,
}))
};
f()
},
9u8 => {
let mut f = || {
let mut channel_id = ChannelId::new_zero();
let mut reason = UpgradableRequired(None);
let mut user_channel_id_low_opt: Option<u64> = None;
let mut user_channel_id_high_opt: Option<u64> = None;
let mut counterparty_node_id = None;
let mut channel_capacity_sats = None;
let mut channel_funding_txo = None;
let mut last_local_balance_msat = None;
read_tlv_fields!(reader, {
(0, channel_id, required),
(1, user_channel_id_low_opt, option),
(2, reason, upgradable_required),
(3, user_channel_id_high_opt, option),
(5, counterparty_node_id, option),
(7, channel_capacity_sats, option),
(9, channel_funding_txo, option),
(11, last_local_balance_msat, option)
});
let user_channel_id = (user_channel_id_low_opt.unwrap_or(0) as u128)
+ ((user_channel_id_high_opt.unwrap_or(0) as u128) << 64);
Ok(Some(Event::ChannelClosed {
channel_id,
user_channel_id,
reason: _init_tlv_based_struct_field!(reason, upgradable_required),
counterparty_node_id,
channel_capacity_sats,
channel_funding_txo,
last_local_balance_msat,
}))
};
f()
},
11u8 => {
let mut f = || {
let mut channel_id = ChannelId::new_zero();
let mut transaction: Option<Transaction> = None;
let mut funding_info: Option<FundingInfo> = None;
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, transaction, option),
(4, funding_info, option),
});
let funding_info = if let Some(tx) = transaction {
FundingInfo::Tx { transaction: tx }
} else {
funding_info.ok_or(msgs::DecodeError::InvalidValue)?
};
Ok(Some(Event::DiscardFunding { channel_id, funding_info }))
};
f()
},
13u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, payment_id, required),
(1, hold_times, optional_vec),
(2, payment_hash, option),
(4, path, required_vec),
(6, blinded_tail, option),
});
let hold_times = hold_times.unwrap_or(Vec::new());
Ok(Some(Event::PaymentPathSuccessful {
payment_id: payment_id.0.unwrap(),
payment_hash,
path: Path { hops: path, blinded_tail },
hold_times,
}))
};
f()
},
15u8 => {
let mut f = || {
let mut payment_hash = PaymentHash([0; 32]);
let mut payment_id = PaymentId([0; 32]);
let mut reason = None;
let mut legacy_reason = None;
let mut invoice_received: Option<bool> = None;
read_tlv_fields!(reader, {
(0, payment_id, required),
(1, legacy_reason, upgradable_option),
(2, payment_hash, required),
(3, invoice_received, option),
(5, reason, upgradable_option),
});
let payment_hash = match invoice_received {
Some(invoice_received) => invoice_received.then(|| payment_hash),
None => (payment_hash != PaymentHash([0; 32])).then(|| payment_hash),
};
let reason = reason.or(legacy_reason);
Ok(Some(Event::PaymentFailed {
payment_id,
payment_hash,
reason: _init_tlv_based_struct_field!(reason, upgradable_option),
}))
};
f()
},
17u8 => {
Ok(None)
},
19u8 => {
let mut f = || {
let mut payment_hash = PaymentHash([0; 32]);
let mut purpose = UpgradableRequired(None);
let mut amount_msat = 0;
let mut receiver_node_id = None;
let mut htlcs: Option<Vec<ClaimedHTLC>> = Some(vec![]);
let mut sender_intended_total_msat: Option<u64> = None;
let mut onion_fields = None;
let mut payment_id = None;
read_tlv_fields!(reader, {
(0, payment_hash, required),
(1, receiver_node_id, option),
(2, purpose, upgradable_required),
(4, amount_msat, required),
(5, htlcs, optional_vec),
(7, sender_intended_total_msat, option),
(9, onion_fields, (option: ReadableArgs,
sender_intended_total_msat.unwrap_or(amount_msat))),
(11, payment_id, option),
});
Ok(Some(Event::PaymentClaimed {
receiver_node_id,
payment_hash,
purpose: _init_tlv_based_struct_field!(purpose, upgradable_required),
amount_msat,
htlcs: htlcs.unwrap_or_default(),
sender_intended_total_msat,
onion_fields,
payment_id,
}))
};
f()
},
21u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, payment_id, required),
(2, payment_hash, required),
(4, path, required_vec),
(6, blinded_tail, option),
});
Ok(Some(Event::ProbeSuccessful {
payment_id: payment_id.0.unwrap(),
payment_hash: payment_hash.0.unwrap(),
path: Path { hops: path, blinded_tail },
}))
};
f()
},
23u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, payment_id, required),
(2, payment_hash, required),
(4, path, required_vec),
(6, short_channel_id, option),
(8, blinded_tail, option),
});
Ok(Some(Event::ProbeFailed {
payment_id: payment_id.0.unwrap(),
payment_hash: payment_hash.0.unwrap(),
path: Path { hops: path, blinded_tail },
short_channel_id,
}))
};
f()
},
25u8 => {
let mut f = || {
let mut prev_channel_id_legacy = ChannelId::new_zero();
let mut failure_reason = None;
let mut failure_type_opt = UpgradableRequired(None);
let mut prev_channel_ids = vec![];
read_tlv_fields!(reader, {
(0, prev_channel_id_legacy, required),
(1, failure_reason, option),
(2, failure_type_opt, upgradable_required),
(3, prev_channel_ids, (default_value, vec![
prev_channel_id_legacy,
])),
});
if let Some(HTLCHandlingFailureType::UnknownNextHop {
requested_forward_scid,
}) = failure_type_opt.0
{
failure_type_opt.0 = Some(HTLCHandlingFailureType::InvalidForward {
requested_forward_scid,
});
failure_reason = Some(LocalHTLCFailureReason::UnknownNextPeer.into());
}
Ok(Some(Event::HTLCHandlingFailed {
prev_channel_ids,
failure_type: _init_tlv_based_struct_field!(
failure_type_opt,
upgradable_required
),
failure_reason,
}))
};
f()
},
27u8 => Ok(None),
29u8 => {
let mut f = || {
let mut channel_id = ChannelId::new_zero();
let mut user_channel_id: u128 = 0;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = None;
let mut channel_type = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(1, funding_txo, option),
(2, user_channel_id, required),
(4, counterparty_node_id, required),
(6, channel_type, required),
});
Ok(Some(Event::ChannelReady {
channel_id,
user_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo,
channel_type: channel_type.0.unwrap(),
}))
};
f()
},
31u8 => {
let mut f = || {
let mut channel_id = ChannelId::new_zero();
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
let mut channel_type = None;
let mut funding_redeem_script = None;
read_tlv_fields!(reader, {
(0, channel_id, required),
(1, channel_type, option),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
(9, funding_redeem_script, option),
});
Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap(),
channel_type,
funding_redeem_script,
}))
};
f()
},
33u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, payment_id, required),
});
Ok(Some(Event::PaymentFailed {
payment_id: payment_id.0.unwrap(),
payment_hash: None,
reason: Some(PaymentFailureReason::InvoiceRequestExpired),
}))
};
f()
},
35u8 => Ok(None),
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});
let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
f()
},
39u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
});
Ok(Some(Event::OnionMessagePeerConnected {
peer_node_id: peer_node_id.0.unwrap(),
}))
};
f()
},
41u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, payment_id, required),
(2, invoice, required),
(4, context, option),
(6, responder, option),
});
Ok(Some(Event::InvoiceReceived {
payment_id: payment_id.0.unwrap(),
invoice: invoice.0.unwrap(),
context,
responder,
}))
};
f()
},
43u8 => {
let mut channel_id = RequiredWrapper(None);
let mut user_channel_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
let mut counterparty_node_id = RequiredWrapper(None);
let mut former_temporary_channel_id = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, funding_txo, required),
(6, counterparty_node_id, required),
(8, former_temporary_channel_id, required)
});
Ok(Some(Event::FundingTxBroadcastSafe {
channel_id: channel_id.0.unwrap(),
user_channel_id: user_channel_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap(),
counterparty_node_id: counterparty_node_id.0.unwrap(),
former_temporary_channel_id: former_temporary_channel_id.0.unwrap(),
}))
},
45u8 => Ok(None),
47u8 => Ok(None),
49u8 => Ok(None),
50u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(1, channel_id, required),
(3, channel_type, required),
(5, user_channel_id, required),
(7, counterparty_node_id, required),
(9, new_funding_txo, required),
(11, new_funding_redeem_script, required),
});
Ok(Some(Event::SpliceNegotiated {
channel_id: channel_id.0.unwrap(),
user_channel_id: user_channel_id.0.unwrap(),
counterparty_node_id: counterparty_node_id.0.unwrap(),
new_funding_txo: new_funding_txo.0.unwrap(),
channel_type: channel_type.0.unwrap(),
new_funding_redeem_script: new_funding_redeem_script.0.unwrap(),
}))
};
f()
},
52u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(1, channel_id, required),
(5, user_channel_id, required),
(7, counterparty_node_id, required),
(11, reason, upgradable_option),
(13, contribution, option),
});
Ok(Some(Event::SpliceNegotiationFailed {
channel_id: channel_id.0.unwrap(),
user_channel_id: user_channel_id.0.unwrap(),
counterparty_node_id: counterparty_node_id.0.unwrap(),
reason: reason.unwrap_or(NegotiationFailureReason::Unknown),
contribution,
}))
};
f()
},
x if x % 2 == 1 => {
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),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn legacy_payment_forwarded_preserves_unknown_inbound_htlc_amount() {
let prev_channel_id = ChannelId::from_bytes([1; 32]);
let next_channel_id = ChannelId::from_bytes([2; 32]);
let mut encoded_legacy_event = vec![
7, 81, 1, 32, ];
encoded_legacy_event.extend_from_slice(&[1; 32]);
encoded_legacy_event.extend_from_slice(&[2, 1, 0]); encoded_legacy_event.extend_from_slice(&[3, 32]); encoded_legacy_event.extend_from_slice(&[2; 32]);
encoded_legacy_event.extend_from_slice(&[5, 8, 0, 0, 0, 0, 0, 45, 198, 192]);
match Event::read(&mut &encoded_legacy_event[..]).unwrap().unwrap() {
Event::PaymentForwarded {
prev_htlcs,
next_htlcs,
total_fee_earned_msat,
outbound_amount_forwarded_msat,
..
} => {
assert_eq!(total_fee_earned_msat, None);
assert_eq!(outbound_amount_forwarded_msat, 3_000_000);
assert_eq!(prev_htlcs.len(), 1);
assert_eq!(prev_htlcs[0].channel_id, prev_channel_id);
assert_eq!(prev_htlcs[0].amount_msat, None);
assert_eq!(next_htlcs.len(), 1);
assert_eq!(next_htlcs[0].channel_id, next_channel_id);
assert_eq!(next_htlcs[0].amount_msat, Some(3_000_000));
},
_ => panic!("expected PaymentForwarded event"),
}
}
}
pub trait EventsProvider {
fn process_pending_events<H: Deref>(&self, handler: H)
where
H::Target: EventHandler;
}
#[derive(Clone, Copy, Debug)]
pub struct ReplayEvent();
pub trait EventHandler {
fn handle_event(&self, event: Event) -> Result<(), ReplayEvent>;
}
impl<F> EventHandler for F
where
F: Fn(Event) -> Result<(), ReplayEvent>,
{
fn handle_event(&self, event: Event) -> Result<(), ReplayEvent> {
self(event)
}
}
impl<T: EventHandler> EventHandler for Arc<T> {
fn handle_event(&self, event: Event) -> Result<(), ReplayEvent> {
self.deref().handle_event(event)
}
}