Skip to content

Commit 2ed1ba6

Browse files
authored
Merge pull request #1138 from TheBlueMatt/2021-10-payee-in-monitors
Store `Payee` info with HTLCs
2 parents ca10349 + 1b99c93 commit 2ed1ba6

File tree

7 files changed

+77
-27
lines changed

7 files changed

+77
-27
lines changed

fuzz/src/chanmon_consistency.rs

+2
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
305305
fee_msat: amt,
306306
cltv_expiry_delta: 200,
307307
}]],
308+
payee: None,
308309
}, payment_hash, &Some(payment_secret)) {
309310
check_payment_err(err);
310311
false
@@ -330,6 +331,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
330331
fee_msat: amt,
331332
cltv_expiry_delta: 200,
332333
}]],
334+
payee: None,
333335
}, payment_hash, &Some(payment_secret)) {
334336
check_payment_err(err);
335337
false

lightning/src/ln/channel.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5768,6 +5768,7 @@ mod tests {
57685768
first_hop_htlc_msat: 548,
57695769
payment_id: PaymentId([42; 32]),
57705770
payment_secret: None,
5771+
payee: None,
57715772
}
57725773
});
57735774

lightning/src/ln/channelmanager.rs

+39-15
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use chain::transaction::{OutPoint, TransactionData};
4545
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
4646
use ln::channel::{Channel, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
4747
use ln::features::{InitFeatures, NodeFeatures};
48-
use routing::router::{Route, RouteHop};
48+
use routing::router::{Payee, PaymentPathRetry, Route, RouteHop};
4949
use ln::msgs;
5050
use ln::msgs::NetAddress;
5151
use ln::onion_utils;
@@ -201,6 +201,7 @@ pub(crate) enum HTLCSource {
201201
first_hop_htlc_msat: u64,
202202
payment_id: PaymentId,
203203
payment_secret: Option<PaymentSecret>,
204+
payee: Option<Payee>,
204205
},
205206
}
206207
#[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
@@ -211,13 +212,14 @@ impl core::hash::Hash for HTLCSource {
211212
0u8.hash(hasher);
212213
prev_hop_data.hash(hasher);
213214
},
214-
HTLCSource::OutboundRoute { path, session_priv, payment_id, payment_secret, first_hop_htlc_msat } => {
215+
HTLCSource::OutboundRoute { path, session_priv, payment_id, payment_secret, first_hop_htlc_msat, payee } => {
215216
1u8.hash(hasher);
216217
path.hash(hasher);
217218
session_priv[..].hash(hasher);
218219
payment_id.hash(hasher);
219220
payment_secret.hash(hasher);
220221
first_hop_htlc_msat.hash(hasher);
222+
payee.hash(hasher);
221223
},
222224
}
223225
}
@@ -231,6 +233,7 @@ impl HTLCSource {
231233
first_hop_htlc_msat: 0,
232234
payment_id: PaymentId([2; 32]),
233235
payment_secret: None,
236+
payee: None,
234237
}
235238
}
236239
}
@@ -2021,7 +2024,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20212024
}
20222025

20232026
// Only public for testing, this should otherwise never be called direcly
2024-
pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
2027+
pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payee: &Option<Payee>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
20252028
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
20262029
let prng_seed = self.keys_manager.get_secure_random_bytes();
20272030
let session_priv_bytes = self.keys_manager.get_secure_random_bytes();
@@ -2071,6 +2074,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20712074
first_hop_htlc_msat: htlc_msat,
20722075
payment_id,
20732076
payment_secret: payment_secret.clone(),
2077+
payee: payee.clone(),
20742078
}, onion_packet, &self.logger),
20752079
channel_state, chan);
20762080

@@ -2209,7 +2213,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22092213
let cur_height = self.best_block.read().unwrap().height() + 1;
22102214
let mut results = Vec::new();
22112215
for path in route.paths.iter() {
2212-
results.push(self.send_payment_along_path(&path, &payment_hash, payment_secret, total_value, cur_height, payment_id, &keysend_preimage));
2216+
results.push(self.send_payment_along_path(&path, &route.payee, &payment_hash, payment_secret, total_value, cur_height, payment_id, &keysend_preimage));
22132217
}
22142218
let mut has_ok = false;
22152219
let mut has_err = false;
@@ -3098,14 +3102,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30983102
self.fail_htlc_backwards_internal(channel_state,
30993103
htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
31003104
},
3101-
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
3105+
HTLCSource::OutboundRoute { session_priv, payment_id, path, payee, .. } => {
31023106
let mut session_priv_bytes = [0; 32];
31033107
session_priv_bytes.copy_from_slice(&session_priv[..]);
31043108
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
31053109
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
3106-
if payment.get_mut().remove(&session_priv_bytes, Some(path.last().unwrap().fee_msat)) &&
3110+
let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
3111+
if payment.get_mut().remove(&session_priv_bytes, Some(path_last_hop.fee_msat)) &&
31073112
!payment.get().is_fulfilled()
31083113
{
3114+
let retry = if let Some(payee_data) = payee {
3115+
Some(PaymentPathRetry {
3116+
payee: payee_data,
3117+
final_value_msat: path_last_hop.fee_msat,
3118+
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
3119+
})
3120+
} else { None };
31093121
self.pending_events.lock().unwrap().push(
31103122
events::Event::PaymentPathFailed {
31113123
payment_hash,
@@ -3114,7 +3126,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31143126
all_paths_failed: payment.get().remaining_parts() == 0,
31153127
path: path.clone(),
31163128
short_channel_id: None,
3117-
retry: None,
3129+
retry,
31183130
#[cfg(test)]
31193131
error_code: None,
31203132
#[cfg(test)]
@@ -3146,13 +3158,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31463158
// from block_connected which may run during initialization prior to the chain_monitor
31473159
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
31483160
match source {
3149-
HTLCSource::OutboundRoute { ref path, session_priv, payment_id, .. } => {
3161+
HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payee, .. } => {
31503162
let mut session_priv_bytes = [0; 32];
31513163
session_priv_bytes.copy_from_slice(&session_priv[..]);
31523164
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
31533165
let mut all_paths_failed = false;
3166+
let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
31543167
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
3155-
if !payment.get_mut().remove(&session_priv_bytes, Some(path.last().unwrap().fee_msat)) {
3168+
if !payment.get_mut().remove(&session_priv_bytes, Some(path_last_hop.fee_msat)) {
31563169
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
31573170
return;
31583171
}
@@ -3167,8 +3180,15 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31673180
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
31683181
return;
31693182
}
3170-
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
31713183
mem::drop(channel_state_lock);
3184+
let retry = if let Some(payee_data) = payee {
3185+
Some(PaymentPathRetry {
3186+
payee: payee_data.clone(),
3187+
final_value_msat: path_last_hop.fee_msat,
3188+
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
3189+
})
3190+
} else { None };
3191+
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
31723192
match &onion_error {
31733193
&HTLCFailReason::LightningError { ref err } => {
31743194
#[cfg(test)]
@@ -3186,7 +3206,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31863206
all_paths_failed,
31873207
path: path.clone(),
31883208
short_channel_id,
3189-
retry: None,
3209+
retry,
31903210
#[cfg(test)]
31913211
error_code: onion_error_code,
31923212
#[cfg(test)]
@@ -3215,7 +3235,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32153235
all_paths_failed,
32163236
path: path.clone(),
32173237
short_channel_id: Some(path.first().unwrap().short_channel_id),
3218-
retry: None,
3238+
retry,
32193239
#[cfg(test)]
32203240
error_code: Some(*failure_code),
32213241
#[cfg(test)]
@@ -5378,12 +5398,14 @@ impl Readable for HTLCSource {
53785398
let mut path = Some(Vec::new());
53795399
let mut payment_id = None;
53805400
let mut payment_secret = None;
5401+
let mut payee = None;
53815402
read_tlv_fields!(reader, {
53825403
(0, session_priv, required),
53835404
(1, payment_id, option),
53845405
(2, first_hop_htlc_msat, required),
53855406
(3, payment_secret, option),
53865407
(4, path, vec_type),
5408+
(5, payee, option),
53875409
});
53885410
if payment_id.is_none() {
53895411
// For backwards compat, if there was no payment_id written, use the session_priv bytes
@@ -5396,6 +5418,7 @@ impl Readable for HTLCSource {
53965418
path: path.unwrap(),
53975419
payment_id: payment_id.unwrap(),
53985420
payment_secret,
5421+
payee,
53995422
})
54005423
}
54015424
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
@@ -5407,7 +5430,7 @@ impl Readable for HTLCSource {
54075430
impl Writeable for HTLCSource {
54085431
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::io::Error> {
54095432
match self {
5410-
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, payment_secret } => {
5433+
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, payment_secret, payee } => {
54115434
0u8.write(writer)?;
54125435
let payment_id_opt = Some(payment_id);
54135436
write_tlv_fields!(writer, {
@@ -5416,6 +5439,7 @@ impl Writeable for HTLCSource {
54165439
(2, first_hop_htlc_msat, required),
54175440
(3, payment_secret, option),
54185441
(4, path, vec_type),
5442+
(5, payee, option),
54195443
});
54205444
}
54215445
HTLCSource::PreviousHopData(ref field) => {
@@ -6160,7 +6184,7 @@ mod tests {
61606184
// Use the utility function send_payment_along_path to send the payment with MPP data which
61616185
// indicates there are more HTLCs coming.
61626186
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
6163-
nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
6187+
nodes[0].node.send_payment_along_path(&route.paths[0], &route.payee, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
61646188
check_added_monitors!(nodes[0], 1);
61656189
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
61666190
assert_eq!(events.len(), 1);
@@ -6190,7 +6214,7 @@ mod tests {
61906214
expect_payment_failed!(nodes[0], our_payment_hash, true);
61916215

61926216
// Send the second half of the original MPP payment.
6193-
nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
6217+
nodes[0].node.send_payment_along_path(&route.paths[0], &route.payee, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
61946218
check_added_monitors!(nodes[0], 1);
61956219
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
61966220
assert_eq!(events.len(), 1);

lightning/src/ln/functional_test_utils.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1114,9 +1114,12 @@ macro_rules! expect_payment_failed_with_update {
11141114
let events = $node.node.get_and_clear_pending_events();
11151115
assert_eq!(events.len(), 1);
11161116
match events[0] {
1117-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, .. } => {
1117+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, .. } => {
11181118
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
11191119
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
1120+
assert!(retry.is_some(), "expected retry.is_some()");
1121+
assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path");
1122+
assert_eq!(retry.as_ref().unwrap().payee.pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path");
11201123
assert!(error_code.is_some(), "expected error_code.is_some() = true");
11211124
assert!(error_data.is_some(), "expected error_data.is_some() = true");
11221125
match network_update {
@@ -1143,9 +1146,12 @@ macro_rules! expect_payment_failed {
11431146
let events = $node.node.get_and_clear_pending_events();
11441147
assert_eq!(events.len(), 1);
11451148
match events[0] {
1146-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, .. } => {
1149+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, ref path, ref retry, .. } => {
11471150
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
11481151
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
1152+
assert!(retry.is_some(), "expected retry.is_some()");
1153+
assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path");
1154+
assert_eq!(retry.as_ref().unwrap().payee.pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path");
11491155
assert!(error_code.is_some(), "expected error_code.is_some() = true");
11501156
assert!(error_data.is_some(), "expected error_data.is_some() = true");
11511157
$(

lightning/src/ln/functional_tests.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ fn fake_network_test() {
919919
});
920920
hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
921921
hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
922-
let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![hops] }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
922+
let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![hops], payee: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
923923

924924
let mut hops = Vec::with_capacity(3);
925925
hops.push(RouteHop {
@@ -948,7 +948,7 @@ fn fake_network_test() {
948948
});
949949
hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
950950
hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
951-
let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![hops] }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
951+
let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![hops], payee: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
952952

953953
// Claim the rebalances...
954954
fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);
@@ -3912,7 +3912,7 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) {
39123912
// indicates there are more HTLCs coming.
39133913
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
39143914
let payment_id = PaymentId([42; 32]);
3915-
nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, cur_height, payment_id, &None).unwrap();
3915+
nodes[0].node.send_payment_along_path(&route.paths[0], &route.payee, &our_payment_hash, &Some(payment_secret), 200000, cur_height, payment_id, &None).unwrap();
39163916
check_added_monitors!(nodes[0], 1);
39173917
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
39183918
assert_eq!(events.len(), 1);

lightning/src/ln/onion_utils.rs

+1
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ mod tests {
555555
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
556556
},
557557
]],
558+
payee: None,
558559
};
559560

560561
let session_priv = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()[..]).unwrap();

lightning/src/routing/router.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ pub struct Route {
7070
/// given path is variable, keeping the length of any path to less than 20 should currently
7171
/// ensure it is viable.
7272
pub paths: Vec<Vec<RouteHop>>,
73+
/// The `payee` parameter passed to [`get_route`].
74+
/// This is used by `ChannelManager` to track information which may be required for retries,
75+
/// provided back to you via [`Event::PaymentPathFailed`].
76+
///
77+
/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
78+
pub payee: Option<Payee>,
7379
}
7480

7581
impl Route {
@@ -106,7 +112,9 @@ impl Writeable for Route {
106112
hop.write(writer)?;
107113
}
108114
}
109-
write_tlv_fields!(writer, {});
115+
write_tlv_fields!(writer, {
116+
(1, self.payee, option),
117+
});
110118
Ok(())
111119
}
112120
}
@@ -124,8 +132,11 @@ impl Readable for Route {
124132
}
125133
paths.push(hops);
126134
}
127-
read_tlv_fields!(reader, {});
128-
Ok(Route { paths })
135+
let mut payee = None;
136+
read_tlv_fields!(reader, {
137+
(1, payee, option),
138+
});
139+
Ok(Route { paths, payee })
129140
}
130141
}
131142

@@ -153,10 +164,10 @@ impl_writeable_tlv_based!(PaymentPathRetry, {
153164
});
154165

155166
/// The recipient of a payment.
156-
#[derive(Clone, Debug)]
167+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
157168
pub struct Payee {
158169
/// The node id of the payee.
159-
pubkey: PublicKey,
170+
pub pubkey: PublicKey,
160171

161172
/// Features supported by the payee.
162173
///
@@ -1442,7 +1453,10 @@ where L::Target: Logger {
14421453
}
14431454
}
14441455

1445-
let route = Route { paths: selected_paths.into_iter().map(|path| path.into_iter().collect()).collect::<Result<Vec<_>, _>>()? };
1456+
let route = Route {
1457+
paths: selected_paths.into_iter().map(|path| path.into_iter().collect()).collect::<Result<Vec<_>, _>>()?,
1458+
payee: Some(payee.clone()),
1459+
};
14461460
log_info!(logger, "Got route to {}: {}", payee.pubkey, log_route!(route));
14471461
Ok(route)
14481462
}
@@ -4600,6 +4614,7 @@ mod tests {
46004614
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
46014615
},
46024616
]],
4617+
payee: None,
46034618
};
46044619

46054620
assert_eq!(route.get_total_fees(), 250);
@@ -4632,6 +4647,7 @@ mod tests {
46324647
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
46334648
},
46344649
]],
4650+
payee: None,
46354651
};
46364652

46374653
assert_eq!(route.get_total_fees(), 200);
@@ -4643,7 +4659,7 @@ mod tests {
46434659
// In an earlier version of `Route::get_total_fees` and `Route::get_total_amount`, they
46444660
// would both panic if the route was completely empty. We test to ensure they return 0
46454661
// here, even though its somewhat nonsensical as a route.
4646-
let route = Route { paths: Vec::new() };
4662+
let route = Route { paths: Vec::new(), payee: None };
46474663

46484664
assert_eq!(route.get_total_fees(), 0);
46494665
assert_eq!(route.get_total_amount(), 0);

0 commit comments

Comments
 (0)