Skip to content

Commit d5dd00f

Browse files
Refactor out decode_next_hop util from ChannelManager::decode_update_add_htlc
This will be used in upcoming commit(s) to facilitate decoding multiple onion layers for multi-node payment receive
1 parent 19fdde2 commit d5dd00f

File tree

2 files changed

+207
-162
lines changed

2 files changed

+207
-162
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 107 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@ use bitcoin::blockdata::constants::genesis_block;
2424
use bitcoin::network::constants::Network;
2525

2626
use bitcoin::hashes::{Hash, HashEngine};
27-
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
2827
use bitcoin::hashes::sha256::Hash as Sha256;
2928
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
30-
use bitcoin::hashes::cmp::fixed_time_eq;
3129
use bitcoin::hash_types::{BlockHash, Txid};
3230

3331
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
@@ -55,15 +53,14 @@ use util::config::UserConfig;
5553
use util::events::{EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
5654
use util::{byte_utils, events};
5755
use util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer};
58-
use util::chacha20::{ChaCha20, ChaChaReader};
5956
use util::logger::{Level, Logger};
6057
use util::errors::APIError;
6158

6259
use io;
6360
use prelude::*;
6461
use core::{cmp, mem};
6562
use core::cell::RefCell;
66-
use io::{Cursor, Read};
63+
use io::Read;
6764
use sync::{Arc, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard};
6865
use core::sync::atomic::{AtomicUsize, Ordering};
6966
use core::time::Duration;
@@ -2088,7 +2085,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20882085
arr.copy_from_slice(&SharedSecret::new(&msg.onion_routing_packet.public_key.unwrap(), &self.our_network_key)[..]);
20892086
arr
20902087
};
2091-
let (rho, mu) = onion_utils::gen_rho_mu_from_shared_secret(&shared_secret);
20922088

20932089
if msg.onion_routing_packet.version != 0 {
20942090
//TODO: Spec doesn't indicate if we should only hash hop_data here (and in other
@@ -2100,13 +2096,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21002096
return_malformed_err!("Unknown onion packet version", 0x8000 | 0x4000 | 4);
21012097
}
21022098

2103-
let mut hmac = HmacEngine::<Sha256>::new(&mu);
2104-
hmac.input(&msg.onion_routing_packet.hop_data);
2105-
hmac.input(&msg.payment_hash.0[..]);
2106-
if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &msg.onion_routing_packet.hmac) {
2107-
return_malformed_err!("HMAC Check failed", 0x8000 | 0x4000 | 5);
2108-
}
2109-
21102099
let mut channel_state = None;
21112100
macro_rules! return_err {
21122101
($msg: expr, $err_code: expr, $data: expr) => {
@@ -2124,164 +2113,122 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21242113
}
21252114
}
21262115

2127-
let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
2128-
let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&msg.onion_routing_packet.hop_data[..]) };
2129-
let (next_hop_data, next_hop_hmac): (msgs::OnionHopData, _) = {
2130-
match <msgs::OnionHopData as Readable>::read(&mut chacha_stream) {
2131-
Err(err) => {
2132-
let error_code = match err {
2133-
msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
2134-
msgs::DecodeError::UnknownRequiredFeature|
2135-
msgs::DecodeError::InvalidValue|
2136-
msgs::DecodeError::ShortRead => 0x4000 | 22, // invalid_onion_payload
2137-
_ => 0x2000 | 2, // Should never happen
2138-
};
2139-
return_err!("Unable to decode our hop data", error_code, &[0;0]);
2140-
},
2141-
Ok(msg) => {
2142-
let mut hmac = [0; 32];
2143-
if let Err(_) = chacha_stream.read_exact(&mut hmac[..]) {
2144-
return_err!("Unable to decode hop data", 0x4000 | 22, &[0;0]);
2145-
}
2146-
(msg, hmac)
2147-
},
2148-
}
2116+
let next_hop = match onion_utils::decode_next_hop(shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, msg.payment_hash) {
2117+
Ok(res) => res,
2118+
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
2119+
return_malformed_err!(err_msg, err_code);
2120+
},
2121+
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code }) => {
2122+
return_err!(err_msg, err_code, &[0; 0]);
2123+
},
21492124
};
21502125

2151-
let pending_forward_info = if next_hop_hmac == [0; 32] {
2152-
#[cfg(test)]
2153-
{
2154-
// In tests, make sure that the initial onion pcket data is, at least, non-0.
2155-
// We could do some fancy randomness test here, but, ehh, whatever.
2156-
// This checks for the issue where you can calculate the path length given the
2157-
// onion data as all the path entries that the originator sent will be here
2158-
// as-is (and were originally 0s).
2159-
// Of course reverse path calculation is still pretty easy given naive routing
2160-
// algorithms, but this fixes the most-obvious case.
2161-
let mut next_bytes = [0; 32];
2162-
chacha_stream.read_exact(&mut next_bytes).unwrap();
2163-
assert_ne!(next_bytes[..], [0; 32][..]);
2164-
chacha_stream.read_exact(&mut next_bytes).unwrap();
2165-
assert_ne!(next_bytes[..], [0; 32][..]);
2166-
}
2167-
2168-
// OUR PAYMENT!
2169-
// final_expiry_too_soon
2170-
// We have to have some headroom to broadcast on chain if we have the preimage, so make sure
2171-
// we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
2172-
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
2173-
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
2174-
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
2175-
if (msg.cltv_expiry as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
2176-
return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]);
2177-
}
2178-
// final_incorrect_htlc_amount
2179-
if next_hop_data.amt_to_forward > msg.amount_msat {
2180-
return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
2181-
}
2182-
// final_incorrect_cltv_expiry
2183-
if next_hop_data.outgoing_cltv_value != msg.cltv_expiry {
2184-
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
2185-
}
2126+
let pending_forward_info = match next_hop {
2127+
onion_utils::Hop::Receive(next_hop_data) => {
2128+
// OUR PAYMENT!
2129+
// final_expiry_too_soon
2130+
// We have to have some headroom to broadcast on chain if we have the preimage, so make sure
2131+
// we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
2132+
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
2133+
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
2134+
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
2135+
if (msg.cltv_expiry as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
2136+
return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]);
2137+
}
2138+
// final_incorrect_htlc_amount
2139+
if next_hop_data.amt_to_forward > msg.amount_msat {
2140+
return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
2141+
}
2142+
// final_incorrect_cltv_expiry
2143+
if next_hop_data.outgoing_cltv_value != msg.cltv_expiry {
2144+
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
2145+
}
21862146

2187-
let routing = match next_hop_data.format {
2188-
msgs::OnionHopDataFormat::Legacy { .. } => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]),
2189-
msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
2190-
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
2191-
if payment_data.is_some() && keysend_preimage.is_some() {
2192-
return_err!("We don't support MPP keysend payments", 0x4000|22, &[0;0]);
2193-
} else if let Some(data) = payment_data {
2194-
PendingHTLCRouting::Receive {
2195-
payment_data: data,
2196-
incoming_cltv_expiry: msg.cltv_expiry,
2197-
}
2198-
} else if let Some(payment_preimage) = keysend_preimage {
2199-
// We need to check that the sender knows the keysend preimage before processing this
2200-
// payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
2201-
// could discover the final destination of X, by probing the adjacent nodes on the route
2202-
// with a keysend payment of identical payment hash to X and observing the processing
2203-
// time discrepancies due to a hash collision with X.
2204-
let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
2205-
if hashed_preimage != msg.payment_hash {
2206-
return_err!("Payment preimage didn't match payment hash", 0x4000|22, &[0;0]);
2207-
}
2147+
let routing = match next_hop_data.format {
2148+
msgs::OnionHopDataFormat::Legacy { .. } => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]),
2149+
msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
2150+
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
2151+
if payment_data.is_some() && keysend_preimage.is_some() {
2152+
return_err!("We don't support MPP keysend payments", 0x4000|22, &[0;0]);
2153+
} else if let Some(data) = payment_data {
2154+
PendingHTLCRouting::Receive {
2155+
payment_data: data,
2156+
incoming_cltv_expiry: msg.cltv_expiry,
2157+
}
2158+
} else if let Some(payment_preimage) = keysend_preimage {
2159+
// We need to check that the sender knows the keysend preimage before processing this
2160+
// payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
2161+
// could discover the final destination of X, by probing the adjacent nodes on the route
2162+
// with a keysend payment of identical payment hash to X and observing the processing
2163+
// time discrepancies due to a hash collision with X.
2164+
let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
2165+
if hashed_preimage != msg.payment_hash {
2166+
return_err!("Payment preimage didn't match payment hash", 0x4000|22, &[0;0]);
2167+
}
22082168

2209-
PendingHTLCRouting::ReceiveKeysend {
2210-
payment_preimage,
2211-
incoming_cltv_expiry: msg.cltv_expiry,
2169+
PendingHTLCRouting::ReceiveKeysend {
2170+
payment_preimage,
2171+
incoming_cltv_expiry: msg.cltv_expiry,
2172+
}
2173+
} else {
2174+
return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
22122175
}
2213-
} else {
2214-
return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
2215-
}
2216-
},
2217-
};
2218-
2219-
// Note that we could obviously respond immediately with an update_fulfill_htlc
2220-
// message, however that would leak that we are the recipient of this payment, so
2221-
// instead we stay symmetric with the forwarding case, only responding (after a
2222-
// delay) once they've send us a commitment_signed!
2223-
2224-
PendingHTLCStatus::Forward(PendingHTLCInfo {
2225-
routing,
2226-
payment_hash: msg.payment_hash.clone(),
2227-
incoming_shared_secret: shared_secret,
2228-
amt_to_forward: next_hop_data.amt_to_forward,
2229-
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2230-
})
2231-
} else {
2232-
let mut new_packet_data = [0; 20*65];
2233-
let read_pos = chacha_stream.read(&mut new_packet_data).unwrap();
2234-
#[cfg(debug_assertions)]
2235-
{
2236-
// Check two things:
2237-
// a) that the behavior of our stream here will return Ok(0) even if the TLV
2238-
// read above emptied out our buffer and the unwrap() wont needlessly panic
2239-
// b) that we didn't somehow magically end up with extra data.
2240-
let mut t = [0; 1];
2241-
debug_assert!(chacha_stream.read(&mut t).unwrap() == 0);
2242-
}
2243-
// Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
2244-
// fill the onion hop data we'll forward to our next-hop peer.
2245-
chacha_stream.chacha.process_in_place(&mut new_packet_data[read_pos..]);
2246-
2247-
let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();
2176+
},
2177+
};
22482178

2249-
let blinding_factor = {
2250-
let mut sha = Sha256::engine();
2251-
sha.input(&new_pubkey.serialize()[..]);
2252-
sha.input(&shared_secret);
2253-
Sha256::from_engine(sha).into_inner()
2254-
};
2179+
// Note that we could obviously respond immediately with an update_fulfill_htlc
2180+
// message, however that would leak that we are the recipient of this payment, so
2181+
// instead we stay symmetric with the forwarding case, only responding (after a
2182+
// delay) once they've send us a commitment_signed!
2183+
2184+
PendingHTLCStatus::Forward(PendingHTLCInfo {
2185+
routing,
2186+
payment_hash: msg.payment_hash.clone(),
2187+
incoming_shared_secret: shared_secret,
2188+
amt_to_forward: next_hop_data.amt_to_forward,
2189+
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2190+
})
2191+
},
2192+
onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
2193+
let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();
2194+
2195+
let blinding_factor = {
2196+
let mut sha = Sha256::engine();
2197+
sha.input(&new_pubkey.serialize()[..]);
2198+
sha.input(&shared_secret);
2199+
Sha256::from_engine(sha).into_inner()
2200+
};
22552201

2256-
let public_key = if let Err(e) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
2257-
Err(e)
2258-
} else { Ok(new_pubkey) };
2202+
let public_key = if let Err(e) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
2203+
Err(e)
2204+
} else { Ok(new_pubkey) };
22592205

2260-
let outgoing_packet = msgs::OnionPacket {
2261-
version: 0,
2262-
public_key,
2263-
hop_data: new_packet_data,
2264-
hmac: next_hop_hmac.clone(),
2265-
};
2206+
let outgoing_packet = msgs::OnionPacket {
2207+
version: 0,
2208+
public_key,
2209+
hop_data: new_packet_bytes,
2210+
hmac: next_hop_hmac.clone(),
2211+
};
22662212

2267-
let short_channel_id = match next_hop_data.format {
2268-
msgs::OnionHopDataFormat::Legacy { short_channel_id } => short_channel_id,
2269-
msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
2270-
msgs::OnionHopDataFormat::FinalNode { .. } => {
2271-
return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0;0]);
2272-
},
2273-
};
2213+
let short_channel_id = match next_hop_data.format {
2214+
msgs::OnionHopDataFormat::Legacy { short_channel_id } => short_channel_id,
2215+
msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
2216+
msgs::OnionHopDataFormat::FinalNode { .. } => {
2217+
return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0;0]);
2218+
},
2219+
};
22742220

2275-
PendingHTLCStatus::Forward(PendingHTLCInfo {
2276-
routing: PendingHTLCRouting::Forward {
2277-
onion_packet: outgoing_packet,
2278-
short_channel_id,
2279-
},
2280-
payment_hash: msg.payment_hash.clone(),
2281-
incoming_shared_secret: shared_secret,
2282-
amt_to_forward: next_hop_data.amt_to_forward,
2283-
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2284-
})
2221+
PendingHTLCStatus::Forward(PendingHTLCInfo {
2222+
routing: PendingHTLCRouting::Forward {
2223+
onion_packet: outgoing_packet,
2224+
short_channel_id,
2225+
},
2226+
payment_hash: msg.payment_hash.clone(),
2227+
incoming_shared_secret: shared_secret,
2228+
amt_to_forward: next_hop_data.amt_to_forward,
2229+
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2230+
})
2231+
}
22852232
};
22862233

22872234
channel_state = Some(self.channel_state.lock().unwrap());

0 commit comments

Comments
 (0)