Skip to content

Commit a0ed02f

Browse files
committed
ln: introduce LocalHTLCFailureReason representing BOLT04
Introduce an enum representation of BOLT04 error codes that will be introduced in the commits that follow, along with being extended to represent failures that are otherwise erased by BOLT04 error codes (insufficient liquidity which is erased by temporary channel failure, for example).
1 parent 75586ba commit a0ed02f

File tree

1 file changed

+182
-5
lines changed

1 file changed

+182
-5
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 182 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,11 +1081,6 @@ where
10811081
let mut is_from_final_non_blinded_node = false;
10821082
let mut hop_hold_times: Vec<u32> = Vec::new();
10831083

1084-
const BADONION: u16 = 0x8000;
1085-
const PERM: u16 = 0x4000;
1086-
const NODE: u16 = 0x2000;
1087-
const UPDATE: u16 = 0x1000;
1088-
10891084
enum ErrorHop<'a> {
10901085
RouteHop(&'a RouteHop),
10911086
TrampolineHop(&'a TrampolineHop),
@@ -1474,6 +1469,188 @@ where
14741469
}
14751470
}
14761471

1472+
const BADONION: u16 = 0x8000;
1473+
const PERM: u16 = 0x4000;
1474+
const NODE: u16 = 0x2000;
1475+
const UPDATE: u16 = 0x1000;
1476+
1477+
/// The reason that a HTLC was failed by the local node. These errors either represent direct,
1478+
/// human-readable mappings of BOLT04 error codes.
1479+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
1480+
pub enum LocalHTLCFailureReason {
1481+
/// There has been a temporary processing failure on the node which may resolve on retry.
1482+
TemporaryNodeFailure,
1483+
/// These has been a permanent processing failure on the node which will not resolve on retry.
1484+
PermanentNodeFailure,
1485+
/// The HTLC does not implement a feature that is required by our node.
1486+
///
1487+
/// The sender may have outdated gossip, or a bug in its implementation.
1488+
RequiredNodeFeature,
1489+
/// The onion version specified by the HTLC packet is unknown to our node.
1490+
InvalidOnionVersion,
1491+
/// The integrity of the HTLC packet cannot be verified because it has an invalid HMAC.
1492+
InvalidOnionHMAC,
1493+
/// The onion packet has an invalid ephemeral key, so the HTLC cannot be processed.
1494+
InvalidOnionKey,
1495+
/// A temporary forwarding error has occurred which may resolve on retry.
1496+
TemporaryChannelFailure,
1497+
/// A permanent forwarding error has occurred which will not resolve on retry.
1498+
PermanentChannelFailure,
1499+
/// The HTLC does not implement a feature that is required by our channel for processing.
1500+
RequiredChannelFeature,
1501+
/// The HTLC's target outgoing channel that is not known to our node.
1502+
UnknownNextPeer,
1503+
/// The HTLC amount is below our advertised htlc_minimum_msat.
1504+
///
1505+
/// The sender may have outdated gossip, or a bug in its implementation.
1506+
AmountBelowMinimum,
1507+
/// The HTLC does not pay sufficient fees.
1508+
///
1509+
/// The sender may have outdated gossip, or a bug in its implementation.
1510+
FeeInsufficient,
1511+
/// The HTLC does not meet the cltv_expiry_delta advertised by our node, set by
1512+
/// [`ChannelConfig::cltv_expiry_delta`].
1513+
///
1514+
/// The sender may have outdated gossip, or a bug in its implementation.
1515+
///
1516+
/// [`ChannelConfig::cltv_expiry_delta`]: crate::util::config::ChannelConfig::cltv_expiry_delta
1517+
IncorrectCLTVExpiry,
1518+
/// The HTLC expires too close to the current block height to be safely processed.
1519+
CLTVExpiryTooSoon,
1520+
/// A payment was made to our node that either had incorrect payment information, or was
1521+
/// unknown to us.
1522+
IncorrectPaymentDetails,
1523+
/// The HTLC's expiry is less than the expiry height specified by the sender.
1524+
///
1525+
/// The forwarding node has either tampered with this value, or the sending node has an
1526+
/// old best block height.
1527+
FinalIncorrectCLTVExpiry,
1528+
/// The HTLC's amount is less than the amount specified by the sender.
1529+
///
1530+
/// The forwarding node has tampered with this value, or has a bug in its implementation.
1531+
FinalIncorrectHTLCAmount,
1532+
/// The channel has been marked as disabled because the channel peer is offline.
1533+
ChannelDisabled,
1534+
/// The HTLC expires too far in the future, so it is rejected to avoid the worst-case outcome
1535+
/// of funds being held for extended periods of time.
1536+
///
1537+
/// Limit set by ['crate::ln::channelmanager::CLTV_FAR_FAR_AWAY`].
1538+
CLTVExpiryTooFar,
1539+
/// The HTLC payload contained in the onion packet could not be understood by our node.
1540+
InvalidOnionPayload,
1541+
/// The total amount for a multi-part payment did not arrive in time, so the HTLCs partially
1542+
/// paying the amount were canceled.
1543+
MPPTimeout,
1544+
/// Our node was selected as part of a blinded path, but the packet we received was not
1545+
/// properly constructed, or had incorrect values for the blinded path.
1546+
///
1547+
/// This may happen if the forwarding node tamperd with the HTLC or the sender or recipient
1548+
/// implementations have a bug.
1549+
InvalidOnionBlinding,
1550+
/// UnknownFailureCode represents BOLT04 failure codes that we are not familiar with. We will
1551+
/// encounter this if:
1552+
/// - A peer sends us a new failure code that LDK has not yet been upgraded to understand.
1553+
/// - We read a deprecated failure code from disk that LDK no longer uses.
1554+
///
1555+
/// See <https://github.com/lightning/bolts/blob/master/04-onion-routing.md#returning-errors>
1556+
/// for latest defined error codes.
1557+
UnknownFailureCode {
1558+
/// The bolt 04 failure code.
1559+
code: u16,
1560+
},
1561+
}
1562+
1563+
impl LocalHTLCFailureReason {
1564+
pub(super) fn failure_code(&self) -> u16 {
1565+
match self {
1566+
Self::TemporaryNodeFailure => NODE | 2,
1567+
Self::PermanentNodeFailure => PERM | NODE | 2,
1568+
Self::RequiredNodeFeature => PERM | NODE | 3,
1569+
Self::InvalidOnionVersion => BADONION | PERM | 4,
1570+
Self::InvalidOnionHMAC => BADONION | PERM | 5,
1571+
Self::InvalidOnionKey => BADONION | PERM | 6,
1572+
Self::TemporaryChannelFailure => UPDATE | 7,
1573+
Self::PermanentChannelFailure => PERM | 8,
1574+
Self::RequiredChannelFeature => PERM | 9,
1575+
Self::UnknownNextPeer => PERM | 10,
1576+
Self::AmountBelowMinimum => UPDATE | 11,
1577+
Self::FeeInsufficient => UPDATE | 12,
1578+
Self::IncorrectCLTVExpiry => UPDATE | 13,
1579+
Self::CLTVExpiryTooSoon => UPDATE | 14,
1580+
Self::IncorrectPaymentDetails => PERM | 15,
1581+
Self::FinalIncorrectCLTVExpiry => 18,
1582+
Self::FinalIncorrectHTLCAmount => 19,
1583+
Self::ChannelDisabled => UPDATE | 20,
1584+
Self::CLTVExpiryTooFar => 21,
1585+
Self::InvalidOnionPayload => PERM | 22,
1586+
Self::MPPTimeout => 23,
1587+
Self::InvalidOnionBlinding => BADONION | PERM | 24,
1588+
Self::UnknownFailureCode { code } => *code,
1589+
}
1590+
}
1591+
1592+
pub(super) fn is_temporary(&self) -> bool {
1593+
self.failure_code() & UPDATE == UPDATE
1594+
}
1595+
1596+
#[cfg(test)]
1597+
pub(super) fn is_permanent(&self) -> bool {
1598+
self.failure_code() & PERM == PERM
1599+
}
1600+
}
1601+
1602+
impl From<u16> for LocalHTLCFailureReason {
1603+
fn from(value: u16) -> Self {
1604+
if value == (NODE | 2) {
1605+
LocalHTLCFailureReason::TemporaryNodeFailure
1606+
} else if value == (PERM | NODE | 2) {
1607+
LocalHTLCFailureReason::PermanentNodeFailure
1608+
} else if value == (PERM | NODE | 3) {
1609+
LocalHTLCFailureReason::RequiredNodeFeature
1610+
} else if value == (BADONION | PERM | 4) {
1611+
LocalHTLCFailureReason::InvalidOnionVersion
1612+
} else if value == (BADONION | PERM | 5) {
1613+
LocalHTLCFailureReason::InvalidOnionHMAC
1614+
} else if value == (BADONION | PERM | 6) {
1615+
LocalHTLCFailureReason::InvalidOnionKey
1616+
} else if value == (UPDATE | 7) {
1617+
LocalHTLCFailureReason::TemporaryChannelFailure
1618+
} else if value == (PERM | 8) {
1619+
LocalHTLCFailureReason::PermanentChannelFailure
1620+
} else if value == (PERM | 9) {
1621+
LocalHTLCFailureReason::RequiredChannelFeature
1622+
} else if value == (PERM | 10) {
1623+
LocalHTLCFailureReason::UnknownNextPeer
1624+
} else if value == (UPDATE | 11) {
1625+
LocalHTLCFailureReason::AmountBelowMinimum
1626+
} else if value == (UPDATE | 12) {
1627+
LocalHTLCFailureReason::FeeInsufficient
1628+
} else if value == (UPDATE | 13) {
1629+
LocalHTLCFailureReason::IncorrectCLTVExpiry
1630+
} else if value == (UPDATE | 14) {
1631+
LocalHTLCFailureReason::CLTVExpiryTooSoon
1632+
} else if value == (PERM | 15) {
1633+
LocalHTLCFailureReason::IncorrectPaymentDetails
1634+
} else if value == 18 {
1635+
LocalHTLCFailureReason::FinalIncorrectCLTVExpiry
1636+
} else if value == 19 {
1637+
LocalHTLCFailureReason::FinalIncorrectHTLCAmount
1638+
} else if value == (UPDATE | 20) {
1639+
LocalHTLCFailureReason::ChannelDisabled
1640+
} else if value == 21 {
1641+
LocalHTLCFailureReason::CLTVExpiryTooFar
1642+
} else if value == (PERM | 22) {
1643+
LocalHTLCFailureReason::InvalidOnionPayload
1644+
} else if value == 23 {
1645+
LocalHTLCFailureReason::MPPTimeout
1646+
} else if value == (BADONION | PERM | 24) {
1647+
LocalHTLCFailureReason::InvalidOnionBlinding
1648+
} else {
1649+
LocalHTLCFailureReason::UnknownFailureCode { code: value }
1650+
}
1651+
}
1652+
}
1653+
14771654
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
14781655
#[cfg_attr(test, derive(PartialEq))]
14791656
pub(super) struct HTLCFailReason(HTLCFailReasonRepr);

0 commit comments

Comments
 (0)