Skip to content

Commit 37d1dac

Browse files
committed
Onion error handling in the origin node
Add PaymentData to HTLCSource to reconstruct incoming_htlc_msat and cltv for each node when an onion failure message is returned. These values are used to check whether an updated chan_update is valid. Refactor onion failure code and more error cases are handled
1 parent 9347366 commit 37d1dac

File tree

1 file changed

+226
-57
lines changed

1 file changed

+226
-57
lines changed

src/ln/channelmanager.rs

Lines changed: 226 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,22 @@ mod channel_held_info {
9898
pub(super) incoming_packet_shared_secret: SharedSecret,
9999
}
100100

101+
/// First hop payment data. This can be used to reconstruct amt_to_forward
102+
/// and outgoing_cltv_value for each hop combined with data from RouteHope
103+
#[derive(Clone)]
104+
pub struct PaymentData {
105+
pub(super) htlc_msat: u64,
106+
pub(super) block_height: u32,
107+
}
108+
101109
/// Tracks the inbound corresponding to an outbound HTLC
102110
#[derive(Clone)]
103111
pub enum HTLCSource {
104112
PreviousHopData(HTLCPreviousHopData),
105113
OutboundRoute {
106114
route: Route,
107115
session_priv: SecretKey,
116+
payment_data: PaymentData,
108117
},
109118
}
110119
#[cfg(test)]
@@ -113,6 +122,7 @@ mod channel_held_info {
113122
HTLCSource::OutboundRoute {
114123
route: Route { hops: Vec::new() },
115124
session_priv: SecretKey::from_slice(&::secp256k1::Secp256k1::without_caps(), &[1; 32]).unwrap(),
125+
payment_data: PaymentData {htlc_msat: 0, block_height: 0},
116126
}
117127
}
118128
}
@@ -1087,6 +1097,10 @@ impl ChannelManager {
10871097
chan.send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
10881098
route: route.clone(),
10891099
session_priv: session_priv.clone(),
1100+
payment_data: PaymentData {
1101+
htlc_msat: htlc_msat,
1102+
block_height: cur_height,
1103+
}
10901104
}, onion_packet).map_err(|he| APIError::RouteError{err: he.err})?
10911105
};
10921106

@@ -1768,7 +1782,210 @@ impl ChannelManager {
17681782
Ok(())
17691783
}
17701784

1785+
// Process onion peacket processed in only in the origin node. Returns update
1786+
// for router and boolean flag indicating if payment can be retried
1787+
fn process_onion_failure(&self, route: &Route, mut packet_decrypted: Vec<u8>, session_priv: &SecretKey, payment_data: &PaymentData) -> Result<(Option<msgs::HTLCFailChannelUpdate>, bool), secp256k1::Error> {
1788+
1789+
macro_rules! onion_failure_log {
1790+
( $error_code_textual: expr, $error_code: expr, $reported_name: expr, $reported_value: expr ) => {
1791+
log_trace!(self, "{}({}) {}({})", $error_code_textual, $error_code, $reported_name, $reported_value);
1792+
};
1793+
( $error_code_textual: expr, $error_code: expr ) => {
1794+
log_trace!(self, "{}({})", $error_code_textual, $error_code);
1795+
};
1796+
}
1797+
1798+
const PERM: u16 = 0x4000;
1799+
const UPDATE: u16 = 0x1000;
1800+
1801+
let mut res = None;
1802+
let mut htlc_msat = payment_data.htlc_msat;
1803+
let mut outgoing_cltv_value = payment_data.block_height;
1804+
1805+
// Handle packed channel/node updates for passing back for the route handler
1806+
Self::construct_onion_keys_callback(&self.secp_ctx, &route, &session_priv, |shared_secret, _, _, route_hop| {
1807+
if res.is_some() { return; }
1808+
1809+
let incoming_htlc_msat = htlc_msat;
1810+
let amt_to_forward = htlc_msat - route_hop.fee_msat;
1811+
htlc_msat = amt_to_forward;
1812+
outgoing_cltv_value += route_hop.cltv_expiry_delta;
1813+
1814+
let ammag = ChannelManager::gen_ammag_from_shared_secret(&shared_secret);
1815+
1816+
let mut decryption_tmp = Vec::with_capacity(packet_decrypted.len());
1817+
decryption_tmp.resize(packet_decrypted.len(), 0);
1818+
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
1819+
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
1820+
packet_decrypted = decryption_tmp;
1821+
1822+
let is_from_final_node = route.hops.last().unwrap().pubkey == route_hop.pubkey;
1823+
1824+
match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
1825+
Err(_e) => {
1826+
res = Some((None, false));
1827+
},
1828+
Ok(ref err_packet) if err_packet.failuremsg.len() < 2 => {
1829+
res = Some((None, false));
1830+
// can't blaim anybody
1831+
},
1832+
Ok(ref err_packet) if err_packet.failuremsg.len() >= 2 => {
1833+
let um = ChannelManager::gen_um_from_shared_secret(&shared_secret);
1834+
1835+
let mut hmac = Hmac::new(Sha256::new(), &um);
1836+
hmac.input(&err_packet.encode()[32..]);
1837+
let mut calc_tag = [0u8; 32];
1838+
hmac.raw_result(&mut calc_tag);
1839+
1840+
if crypto::util::fixed_time_eq(&calc_tag, &err_packet.hmac) {
1841+
let error_code = byte_utils::slice_to_be16(&err_packet.failuremsg[0..2]);
1842+
1843+
match error_code & 0xff {
1844+
1|2|3 => {
1845+
// either from an intermediate or final node
1846+
// invalid_realm(PERM|1),
1847+
// temporary_node_failure(NODE|2)
1848+
// permanent_node_failure(PERM|NODE|2)
1849+
// required_node_feature_mssing(PERM|NODE|3)
1850+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1851+
node_id: route_hop.pubkey,
1852+
is_permanent: error_code & PERM == PERM,
1853+
}), !(error_code & PERM == PERM && is_from_final_node)));
1854+
// node returning invalid_realm is removed from network_map,
1855+
// although NODE flag is not set, TODO: or remove channel only?
1856+
// retry payment when removed node is not a final node
1857+
return;
1858+
},
1859+
_ => {}
1860+
}
1861+
1862+
if is_from_final_node {
1863+
let payment_retryable = match error_code {
1864+
c if c == PERM|15 => false, // unknown_payment_hash
1865+
c if c == PERM|16 => false, // incorrect_payment_amount
1866+
17 => true, // final_expiry_too_soon
1867+
18 if err_packet.failuremsg.len() == 6 => { // final_incorrect_cltv_expiry
1868+
let _reported_cltv_expiry = byte_utils::slice_to_be16(&err_packet.failuremsg[2..2+4]);
1869+
true
1870+
},
1871+
19 if err_packet.failuremsg.len() == 6 => { // final_incorrect_htlc_amount
1872+
let _reported_incoming_htlc_msat = byte_utils::slice_to_be16(&err_packet.failuremsg[2..2+4]);
1873+
true
1874+
},
1875+
_ => {
1876+
// A final node has sent us either an invalid code or an error_code that
1877+
// MUST be sent from the processing node, or the formmat of failuremsg
1878+
// does not coform to the spec.
1879+
// Remove it from the network map and don't may retry payment
1880+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1881+
node_id: route_hop.pubkey,
1882+
is_permanent: true,
1883+
}), false));
1884+
return;
1885+
}
1886+
};
1887+
debug_assert_eq!(payment_retryable, error_code&PERM != PERM);
1888+
res = Some((None, payment_retryable));
1889+
return;
1890+
}
1891+
1892+
// now, error_code should be only from the intermediate nodes
1893+
match error_code {
1894+
_c if error_code & PERM == PERM => {
1895+
res = Some((Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
1896+
short_channel_id: route_hop.short_channel_id,
1897+
is_permanent: true,
1898+
}), false));
1899+
},
1900+
_c if error_code & UPDATE == UPDATE => {
1901+
let mut pos = 2;
1902+
pos += match error_code {
1903+
c if c == UPDATE|7 => 0, // temporary_channel_failure
1904+
c if c == UPDATE|11 => 8, // amount_below_minimum
1905+
c if c == UPDATE|12 => 8, // fee_insufficient
1906+
c if c == UPDATE|13 => 4, // incorrect_cltv_expiry
1907+
c if c == UPDATE|20 => 2, // channel_disabled
1908+
c if c == UPDATE|21 => 0, // expiry_too_far
1909+
_ => {
1910+
// node sending unknown code
1911+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1912+
node_id: route_hop.pubkey,
1913+
is_permanent: true,
1914+
}), false));
1915+
return;
1916+
}
1917+
};
1918+
1919+
if err_packet.failuremsg.len() >= pos+2 {
1920+
let update_len = byte_utils::slice_to_be16(&err_packet.failuremsg[pos+2..pos+4]) as usize;
1921+
if err_packet.failuremsg.len() >= pos+4 + update_len {
1922+
if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&err_packet.failuremsg[pos+4..pos+4+update_len])) {
1923+
if chan_update.contents.timestamp <= route_hop.channel_update_timestamp {
1924+
res = Some((None, true));
1925+
return;
1926+
}
1927+
// if channel_update should NOT have caused the failure:
1928+
// MAY treat the channel_update as invalid.
1929+
let is_chan_update_invalid = match error_code {
1930+
c if c == UPDATE|11 => { // amount_below_minimum
1931+
let reported_htlc_msat = byte_utils::slice_to_be16(&err_packet.failuremsg[2..2+8]);
1932+
onion_failure_log!("amount_below_minimum", UPDATE|11, "htlc_msat", reported_htlc_msat);
1933+
incoming_htlc_msat > chan_update.contents.htlc_minimum_msat
1934+
},
1935+
c if c == UPDATE|12 => { // fee_insufficient
1936+
let reported_htlc_msat = byte_utils::slice_to_be16(&err_packet.failuremsg[2..2+8]);
1937+
let new_fee = amt_to_forward.checked_mul(chan_update.contents.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan_update.contents.fee_base_msat as u64) });
1938+
onion_failure_log!("fee_insufficient", UPDATE|12, "htlc_msat", reported_htlc_msat);
1939+
new_fee.is_none() || incoming_htlc_msat >= new_fee.unwrap() && incoming_htlc_msat >= amt_to_forward + new_fee.unwrap()
1940+
}
1941+
c if c == UPDATE|13 => { // incorrect_cltv_expiry
1942+
let reported_cltv_expiry = byte_utils::slice_to_be16(&err_packet.failuremsg[2..2+4]);
1943+
onion_failure_log!("incorrect_cltv_expiry", UPDATE|13, "cltv_expiry", reported_cltv_expiry);
1944+
route_hop.cltv_expiry_delta as u16 >= chan_update.contents.cltv_expiry_delta
1945+
},
1946+
c if c == UPDATE|20 => { // channel_disabled
1947+
let reported_flags = byte_utils::slice_to_be16(&err_packet.failuremsg[2..2+2]);
1948+
onion_failure_log!("channel_disabled", UPDATE|20, "flags", reported_flags);
1949+
chan_update.contents.flags & 0x01 == 0x01
1950+
},
1951+
c if c == UPDATE|21 => true, // expiry_too_far
1952+
_ => { unreachable!(); },
1953+
};
1954+
1955+
let msg = if is_chan_update_invalid { None } else {
1956+
Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
1957+
msg: chan_update,
1958+
})
1959+
};
1960+
res = Some((msg, true));
1961+
return;
1962+
}
1963+
}
1964+
}
1965+
},
1966+
14 => { // expiry_too_soon
1967+
res = Some((None, true));
1968+
return;
1969+
}
1970+
_ => {
1971+
// node sending unknown code
1972+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1973+
node_id: route_hop.pubkey,
1974+
is_permanent: true,
1975+
}), false));
1976+
return;
1977+
}
1978+
}
1979+
}
1980+
},
1981+
_ => { unreachable!() }
1982+
}
1983+
})?;
1984+
Ok(res.unwrap())
1985+
}
1986+
17711987
fn internal_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, MsgHandleErrInternal> {
1988+
17721989
let mut channel_state = self.channel_state.lock().unwrap();
17731990
let htlc_source = match channel_state.by_id.get_mut(&msg.channel_id) {
17741991
Some(chan) => {
@@ -1782,63 +1999,15 @@ impl ChannelManager {
17821999
None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
17832000
}?;
17842001

1785-
match htlc_source {
1786-
&HTLCSource::OutboundRoute { ref route, ref session_priv, .. } => {
1787-
// Handle packed channel/node updates for passing back for the route handler
1788-
let mut packet_decrypted = msg.reason.data.clone();
1789-
let mut res = None;
1790-
Self::construct_onion_keys_callback(&self.secp_ctx, &route, &session_priv, |shared_secret, _, _, route_hop| {
1791-
if res.is_some() { return; }
1792-
1793-
let ammag = ChannelManager::gen_ammag_from_shared_secret(&shared_secret);
1794-
1795-
let mut decryption_tmp = Vec::with_capacity(packet_decrypted.len());
1796-
decryption_tmp.resize(packet_decrypted.len(), 0);
1797-
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
1798-
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
1799-
packet_decrypted = decryption_tmp;
1800-
1801-
if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
1802-
if err_packet.failuremsg.len() >= 2 {
1803-
let um = ChannelManager::gen_um_from_shared_secret(&shared_secret);
1804-
1805-
let mut hmac = Hmac::new(Sha256::new(), &um);
1806-
hmac.input(&err_packet.encode()[32..]);
1807-
let mut calc_tag = [0u8; 32];
1808-
hmac.raw_result(&mut calc_tag);
1809-
if crypto::util::fixed_time_eq(&calc_tag, &err_packet.hmac) {
1810-
const UNKNOWN_CHAN: u16 = 0x4000|10;
1811-
const TEMP_CHAN_FAILURE: u16 = 0x4000|7;
1812-
match byte_utils::slice_to_be16(&err_packet.failuremsg[0..2]) {
1813-
TEMP_CHAN_FAILURE => {
1814-
if err_packet.failuremsg.len() >= 4 {
1815-
let update_len = byte_utils::slice_to_be16(&err_packet.failuremsg[2..4]) as usize;
1816-
if err_packet.failuremsg.len() >= 4 + update_len {
1817-
if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&err_packet.failuremsg[4..4 + update_len])) {
1818-
res = Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
1819-
msg: chan_update,
1820-
});
1821-
}
1822-
}
1823-
}
1824-
},
1825-
UNKNOWN_CHAN => {
1826-
// No such next-hop. We know this came from the
1827-
// current node as the HMAC validated.
1828-
res = Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
1829-
short_channel_id: route_hop.short_channel_id,
1830-
is_permanent: true,
1831-
});
1832-
},
1833-
_ => {}, //TODO: Enumerate all of these!
1834-
}
1835-
}
1836-
}
1837-
}
1838-
}).unwrap();
1839-
Ok(res)
1840-
},
1841-
_ => { Ok(None) },
2002+
// we are the origin node and update route information
2003+
// also determine if the payment is retryable
2004+
if let &HTLCSource::OutboundRoute { ref route, ref session_priv, ref payment_data} = htlc_source {
2005+
let (channel_update, _payment_retry) = self.process_onion_failure(route, msg.reason.data.clone(), session_priv, payment_data).unwrap();
2006+
Ok(channel_update)
2007+
// TODO: include pyament_retry info in PaymentFailed event that will be
2008+
// fired when receiving revoke_and_ack
2009+
} else {
2010+
Ok(None)
18422011
}
18432012
}
18442013

0 commit comments

Comments
 (0)