Skip to content

Commit 8572fdf

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 5655590 commit 8572fdf

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;
@@ -2084,7 +2081,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20842081
arr.copy_from_slice(&SharedSecret::new(&msg.onion_routing_packet.public_key.unwrap(), &self.our_network_key)[..]);
20852082
arr
20862083
};
2087-
let (rho, mu) = onion_utils::gen_rho_mu_from_shared_secret(&shared_secret);
20882084

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

2099-
let mut hmac = HmacEngine::<Sha256>::new(&mu);
2100-
hmac.input(&msg.onion_routing_packet.hop_data);
2101-
hmac.input(&msg.payment_hash.0[..]);
2102-
if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &msg.onion_routing_packet.hmac) {
2103-
return_malformed_err!("HMAC Check failed", 0x8000 | 0x4000 | 5);
2104-
}
2105-
21062095
let mut channel_state = None;
21072096
macro_rules! return_err {
21082097
($msg: expr, $err_code: expr, $data: expr) => {
@@ -2120,164 +2109,122 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21202109
}
21212110
}
21222111

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

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

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

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

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

2252-
let public_key = if let Err(e) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
2253-
Err(e)
2254-
} else { Ok(new_pubkey) };
2198+
let public_key = if let Err(e) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
2199+
Err(e)
2200+
} else { Ok(new_pubkey) };
22552201

2256-
let outgoing_packet = msgs::OnionPacket {
2257-
version: 0,
2258-
public_key,
2259-
hop_data: new_packet_data,
2260-
hmac: next_hop_hmac.clone(),
2261-
};
2202+
let outgoing_packet = msgs::OnionPacket {
2203+
version: 0,
2204+
public_key,
2205+
hop_data: new_packet_bytes,
2206+
hmac: next_hop_hmac.clone(),
2207+
};
22622208

2263-
let short_channel_id = match next_hop_data.format {
2264-
msgs::OnionHopDataFormat::Legacy { short_channel_id } => short_channel_id,
2265-
msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
2266-
msgs::OnionHopDataFormat::FinalNode { .. } => {
2267-
return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0;0]);
2268-
},
2269-
};
2209+
let short_channel_id = match next_hop_data.format {
2210+
msgs::OnionHopDataFormat::Legacy { short_channel_id } => short_channel_id,
2211+
msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
2212+
msgs::OnionHopDataFormat::FinalNode { .. } => {
2213+
return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0;0]);
2214+
},
2215+
};
22702216

2271-
PendingHTLCStatus::Forward(PendingHTLCInfo {
2272-
routing: PendingHTLCRouting::Forward {
2273-
onion_packet: outgoing_packet,
2274-
short_channel_id,
2275-
},
2276-
payment_hash: msg.payment_hash.clone(),
2277-
incoming_shared_secret: shared_secret,
2278-
amt_to_forward: next_hop_data.amt_to_forward,
2279-
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2280-
})
2217+
PendingHTLCStatus::Forward(PendingHTLCInfo {
2218+
routing: PendingHTLCRouting::Forward {
2219+
onion_packet: outgoing_packet,
2220+
short_channel_id,
2221+
},
2222+
payment_hash: msg.payment_hash.clone(),
2223+
incoming_shared_secret: shared_secret,
2224+
amt_to_forward: next_hop_data.amt_to_forward,
2225+
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2226+
})
2227+
}
22812228
};
22822229

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

0 commit comments

Comments
 (0)