use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use ln::channel::{Channel, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
use ln::features::{InitFeatures, NodeFeatures};
-use routing::router::{Payee, Route, RouteHop, RoutePath, RouteParameters};
+use routing::router::{PaymentParameters, Route, RouteHop, RoutePath, RouteParameters};
use ln::msgs;
use ln::msgs::NetAddress;
use ln::onion_utils;
use std::time::Instant;
mod inbound_payment {
+ use alloc::string::ToString;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::cmp::fixed_time_eq;
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
use chain::keysinterface::{KeyMaterial, KeysInterface, Sign};
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
+ use ln::channelmanager::APIError;
use ln::msgs;
use ln::msgs::MAX_VALUE_MSAT;
use util::chacha20::ChaCha20;
impl ExpandedKey {
pub(super) fn new(key_material: &KeyMaterial) -> ExpandedKey {
- hkdf_extract_expand(&vec![0], &key_material)
+ hkdf_extract_expand(b"LDK Inbound Payment Key Expansion", &key_material)
}
}
/// [`KeysInterface::get_inbound_payment_key_material`]: crate::chain::keysinterface::KeysInterface::get_inbound_payment_key_material
/// [`create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
/// [`create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
- pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: msgs::FinalOnionHopData, highest_seen_timestamp: u64, keys: &ExpandedKey, logger: &L) -> Result<Option<PaymentPreimage>, ()>
+ pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: &msgs::FinalOnionHopData, highest_seen_timestamp: u64, keys: &ExpandedKey, logger: &L) -> Result<Option<PaymentPreimage>, ()>
where L::Target: Logger
{
- let mut iv_bytes = [0; IV_LEN];
- let (iv_slice, encrypted_metadata_bytes) = payment_data.payment_secret.0.split_at(IV_LEN);
- iv_bytes.copy_from_slice(iv_slice);
-
- let chacha_block = ChaCha20::get_single_block(&keys.metadata_key, &iv_bytes);
- let mut metadata_bytes: [u8; METADATA_LEN] = [0; METADATA_LEN];
- for i in 0..METADATA_LEN {
- metadata_bytes[i] = chacha_block[i] ^ encrypted_metadata_bytes[i];
- }
+ let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_data.payment_secret, keys);
let payment_type_res = Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET);
let mut amt_msat_bytes = [0; AMT_MSAT_LEN];
}
},
Ok(Method::LdkPaymentHash) => {
- let mut hmac = HmacEngine::<Sha256>::new(&keys.ldk_pmt_hash_key);
- hmac.input(&iv_bytes);
- hmac.input(&metadata_bytes);
- let decoded_payment_preimage = Hmac::from_engine(hmac).into_inner();
- if !fixed_time_eq(&payment_hash.0, &Sha256::hash(&decoded_payment_preimage).into_inner()) {
- log_trace!(logger, "Failing HTLC with payment_hash {}: payment preimage {} did not match", log_bytes!(payment_hash.0), log_bytes!(decoded_payment_preimage));
- return Err(())
+ match derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys) {
+ Ok(preimage) => payment_preimage = Some(preimage),
+ Err(bad_preimage_bytes) => {
+ log_trace!(logger, "Failing HTLC with payment_hash {} due to mismatching preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes));
+ return Err(())
+ }
}
- payment_preimage = Some(PaymentPreimage(decoded_payment_preimage));
},
Err(unknown_bits) => {
log_trace!(logger, "Failing HTLC with payment hash {} due to unknown payment type {}", log_bytes!(payment_hash.0), unknown_bits);
Ok(payment_preimage)
}
+ pub(super) fn get_payment_preimage(payment_hash: PaymentHash, payment_secret: PaymentSecret, keys: &ExpandedKey) -> Result<PaymentPreimage, APIError> {
+ let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_secret, keys);
+
+ match Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET) {
+ Ok(Method::LdkPaymentHash) => {
+ derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys)
+ .map_err(|bad_preimage_bytes| APIError::APIMisuseError {
+ err: format!("Payment hash {} did not match decoded preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes))
+ })
+ },
+ Ok(Method::UserPaymentHash) => Err(APIError::APIMisuseError {
+ err: "Expected payment type to be LdkPaymentHash, instead got UserPaymentHash".to_string()
+ }),
+ Err(other) => Err(APIError::APIMisuseError { err: format!("Unknown payment type: {}", other) }),
+ }
+ }
+
+ fn decrypt_metadata(payment_secret: PaymentSecret, keys: &ExpandedKey) -> ([u8; IV_LEN], [u8; METADATA_LEN]) {
+ let mut iv_bytes = [0; IV_LEN];
+ let (iv_slice, encrypted_metadata_bytes) = payment_secret.0.split_at(IV_LEN);
+ iv_bytes.copy_from_slice(iv_slice);
+
+ let chacha_block = ChaCha20::get_single_block(&keys.metadata_key, &iv_bytes);
+ let mut metadata_bytes: [u8; METADATA_LEN] = [0; METADATA_LEN];
+ for i in 0..METADATA_LEN {
+ metadata_bytes[i] = chacha_block[i] ^ encrypted_metadata_bytes[i];
+ }
+
+ (iv_bytes, metadata_bytes)
+ }
+
+ // Errors if the payment preimage doesn't match `payment_hash`. Returns the bad preimage bytes in
+ // this case.
+ fn derive_ldk_payment_preimage(payment_hash: PaymentHash, iv_bytes: &[u8; IV_LEN], metadata_bytes: &[u8; METADATA_LEN], keys: &ExpandedKey) -> Result<PaymentPreimage, [u8; 32]> {
+ let mut hmac = HmacEngine::<Sha256>::new(&keys.ldk_pmt_hash_key);
+ hmac.input(iv_bytes);
+ hmac.input(metadata_bytes);
+ let decoded_payment_preimage = Hmac::from_engine(hmac).into_inner();
+ if !fixed_time_eq(&payment_hash.0, &Sha256::hash(&decoded_payment_preimage).into_inner()) {
+ return Err(decoded_payment_preimage);
+ }
+ return Ok(PaymentPreimage(decoded_payment_preimage))
+ }
+
fn hkdf_extract_expand(salt: &[u8], ikm: &KeyMaterial) -> ExpandedKey {
let mut hmac = HmacEngine::<Sha256>::new(salt);
hmac.input(&ikm.0);
/// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
/// are part of the same payment.
- Invoice(msgs::FinalOnionHopData),
+ Invoice {
+ /// This is only here for backwards-compatibility in serialization, in the future it can be
+ /// removed, breaking clients running 0.0.104 and earlier.
+ _legacy_hop_data: msgs::FinalOnionHopData,
+ },
/// Contains the payer-provided preimage.
Spontaneous(PaymentPreimage),
}
cltv_expiry: u32,
value: u64,
onion_payload: OnionPayload,
+ total_msat: u64,
}
/// A payment identifier used to uniquely identify a payment to LDK.
first_hop_htlc_msat: u64,
payment_id: PaymentId,
payment_secret: Option<PaymentSecret>,
- payee: Option<Payee>,
+ payment_params: Option<PaymentParameters>,
},
}
#[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
- HTLCSource::OutboundRoute { path, session_priv, payment_id, payment_secret, first_hop_htlc_msat, payee } => {
+ HTLCSource::OutboundRoute { path, session_priv, payment_id, payment_secret, first_hop_htlc_msat, payment_params } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
payment_secret.hash(hasher);
first_hop_htlc_msat.hash(hasher);
- payee.hash(hasher);
+ payment_params.hash(hasher);
},
}
}
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
payment_secret: None,
- payee: None,
+ payment_params: None,
}
}
}
Self {
err: match err {
ChannelError::Warn(msg) => LightningError {
- err: msg,
- action: msgs::ErrorAction::IgnoreError,
+ err: msg.clone(),
+ action: msgs::ErrorAction::SendWarningMessage {
+ msg: msgs::WarningMessage {
+ channel_id,
+ data: msg
+ },
+ log_level: Level::Warn,
+ },
},
ChannelError::Ignore(msg) => LightningError {
err: msg,
($self: ident, $err: expr, $short_to_id: expr, $channel: expr, $channel_id: expr) => {
match $err {
ChannelError::Warn(msg) => {
- //TODO: Once warning messages are merged, we should send a `warning` message to our
- //peer here.
- (false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore(msg), $channel_id.clone()))
+ (false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Warn(msg), $channel_id.clone()))
},
ChannelError::Ignore(msg) => {
(false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore(msg), $channel_id.clone()))
}
// Only public for testing, this should otherwise never be called direcly
- 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> {
+ pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_params: &Option<PaymentParameters>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
let prng_seed = self.keys_manager.get_secure_random_bytes();
let session_priv_bytes = self.keys_manager.get_secure_random_bytes();
first_hop_htlc_msat: htlc_msat,
payment_id,
payment_secret: payment_secret.clone(),
- payee: payee.clone(),
+ payment_params: payment_params.clone(),
}, onion_packet, &self.logger),
channel_state, chan)
} {
let cur_height = self.best_block.read().unwrap().height() + 1;
let mut results = Vec::new();
for path in route.paths.iter() {
- results.push(self.send_payment_along_path(&path, &route.payee, &payment_hash, payment_secret, total_value, cur_height, payment_id, &keysend_preimage));
+ results.push(self.send_payment_along_path(&path, &route.payment_params, &payment_hash, payment_secret, total_value, cur_height, payment_id, &keysend_preimage));
}
let mut has_ok = false;
let mut has_err = false;
results,
payment_id,
failed_paths_retry: if pending_amt_unsent != 0 {
- if let Some(payee) = &route.payee {
+ if let Some(payment_params) = &route.payment_params {
Some(RouteParameters {
- payee: payee.clone(),
+ payment_params: payment_params.clone(),
final_value_msat: pending_amt_unsent,
final_cltv_expiry_delta: max_unsent_cltv_delta,
})
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
routing, incoming_shared_secret, payment_hash, amt_to_forward, .. },
prev_funding_outpoint } => {
- let (cltv_expiry, onion_payload) = match routing {
- PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry } =>
- (incoming_cltv_expiry, OnionPayload::Invoice(payment_data)),
+ let (cltv_expiry, onion_payload, payment_data) = match routing {
+ PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry } => {
+ let _legacy_hop_data = msgs::FinalOnionHopData {
+ payment_secret: payment_data.payment_secret,
+ total_msat: payment_data.total_msat
+ };
+ (incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data }, Some(payment_data))
+ },
PendingHTLCRouting::ReceiveKeysend { payment_preimage, incoming_cltv_expiry } =>
- (incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage)),
+ (incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage), None),
_ => {
panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
}
incoming_packet_shared_secret: incoming_shared_secret,
},
value: amt_to_forward,
+ total_msat: if let Some(data) = &payment_data { data.total_msat } else { amt_to_forward },
cltv_expiry,
onion_payload,
};
}
macro_rules! check_total_value {
- ($payment_data_total_msat: expr, $payment_secret: expr, $payment_preimage: expr) => {{
- let mut total_value = 0;
+ ($payment_data: expr, $payment_preimage: expr) => {{
let mut payment_received_generated = false;
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
.or_insert(Vec::new());
continue
}
}
- htlcs.push(claimable_htlc);
+ let mut total_value = claimable_htlc.value;
for htlc in htlcs.iter() {
total_value += htlc.value;
match &htlc.onion_payload {
- OnionPayload::Invoice(htlc_payment_data) => {
- if htlc_payment_data.total_msat != $payment_data_total_msat {
+ OnionPayload::Invoice { .. } => {
+ if htlc.total_msat != claimable_htlc.total_msat {
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
- log_bytes!(payment_hash.0), $payment_data_total_msat, htlc_payment_data.total_msat);
+ log_bytes!(payment_hash.0), claimable_htlc.total_msat, htlc.total_msat);
total_value = msgs::MAX_VALUE_MSAT;
}
if total_value >= msgs::MAX_VALUE_MSAT { break; }
_ => unreachable!(),
}
}
- if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data_total_msat {
+ if total_value >= msgs::MAX_VALUE_MSAT || total_value > claimable_htlc.total_msat {
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
- log_bytes!(payment_hash.0), total_value, $payment_data_total_msat);
+ log_bytes!(payment_hash.0), total_value, claimable_htlc.total_msat);
for htlc in htlcs.iter() {
fail_htlc!(htlc);
}
- } else if total_value == $payment_data_total_msat {
+ } else if total_value == claimable_htlc.total_msat {
new_events.push(events::Event::PaymentReceived {
payment_hash,
purpose: events::PaymentPurpose::InvoicePayment {
payment_preimage: $payment_preimage,
- payment_secret: $payment_secret,
+ payment_secret: $payment_data.payment_secret,
},
amt: total_value,
});
// payment value yet, wait until we receive more
// MPP parts.
}
+ htlcs.push(claimable_htlc);
payment_received_generated
}}
}
match payment_secrets.entry(payment_hash) {
hash_map::Entry::Vacant(_) => {
match claimable_htlc.onion_payload {
- OnionPayload::Invoice(ref payment_data) => {
- let payment_preimage = match inbound_payment::verify(payment_hash, payment_data.clone(), self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
+ OnionPayload::Invoice { .. } => {
+ let payment_data = payment_data.unwrap();
+ let payment_preimage = match inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
Ok(payment_preimage) => payment_preimage,
Err(()) => {
fail_htlc!(claimable_htlc);
continue
}
};
- let payment_data_total_msat = payment_data.total_msat;
- let payment_secret = payment_data.payment_secret.clone();
- check_total_value!(payment_data_total_msat, payment_secret, payment_preimage);
+ check_total_value!(payment_data, payment_preimage);
},
OnionPayload::Spontaneous(preimage) => {
match channel_state.claimable_htlcs.entry(payment_hash) {
}
},
hash_map::Entry::Occupied(inbound_payment) => {
- let payment_data =
- if let OnionPayload::Invoice(ref data) = claimable_htlc.onion_payload {
- data.clone()
- } else {
- log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
- fail_htlc!(claimable_htlc);
- continue
- };
+ if payment_data.is_none() {
+ log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
+ fail_htlc!(claimable_htlc);
+ continue
+ };
+ let payment_data = payment_data.unwrap();
if inbound_payment.get().payment_secret != payment_data.payment_secret {
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", log_bytes!(payment_hash.0));
fail_htlc!(claimable_htlc);
log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
fail_htlc!(claimable_htlc);
} else {
- let payment_received_generated = check_total_value!(payment_data.total_msat, payment_data.payment_secret, inbound_payment.get().payment_preimage);
+ let payment_received_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage);
if payment_received_generated {
inbound_payment.remove_entry();
}
self.fail_htlc_backwards_internal(channel_state,
htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
},
- HTLCSource::OutboundRoute { session_priv, payment_id, path, payee, .. } => {
+ HTLCSource::OutboundRoute { session_priv, payment_id, path, payment_params, .. } => {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
if payment.get_mut().remove(&session_priv_bytes, Some(&path)) && !payment.get().is_fulfilled() {
- let retry = if let Some(payee_data) = payee {
+ let retry = if let Some(payment_params_data) = payment_params {
let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
Some(RouteParameters {
- payee: payee_data,
+ payment_params: payment_params_data,
final_value_msat: path_last_hop.fee_msat,
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
})
// from block_connected which may run during initialization prior to the chain_monitor
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
- HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payee, .. } => {
+ HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payment_params, .. } => {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
return;
}
mem::drop(channel_state_lock);
- let retry = if let Some(payee_data) = payee {
+ let retry = if let Some(payment_params_data) = payment_params {
let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
Some(RouteParameters {
- payee: payee_data.clone(),
+ payment_params: payment_params_data.clone(),
final_value_msat: path_last_hop.fee_msat,
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
})
}
fn finalize_claims(&self, mut sources: Vec<HTLCSource>) {
+ let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let mut pending_events = self.pending_events.lock().unwrap();
for source in sources.drain(..) {
if let HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } = source {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
- let mut outbounds = self.pending_outbound_payments.lock().unwrap();
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
assert!(payment.get().is_fulfilled());
if payment.get_mut().remove(&session_priv_bytes, None) {
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
/// the `min_value_msat` provided here, if one is provided.
///
- /// The [`PaymentHash`] (and corresponding [`PaymentPreimage`]) must be globally unique. This
- /// method may return an Err if another payment with the same payment_hash is still pending.
+ /// The [`PaymentHash`] (and corresponding [`PaymentPreimage`]) should be globally unique, though
+ /// note that LDK will not stop you from registering duplicate payment hashes for inbound
+ /// payments.
///
/// `min_value_msat` should be set if the invoice being generated contains a value. Any payment
/// received for the returned [`PaymentHash`] will be required to be at least `min_value_msat`
self.set_payment_hash_secret_map(payment_hash, None, min_value_msat, invoice_expiry_delta_secs)
}
+ /// Gets an LDK-generated payment preimage from a payment hash and payment secret that were
+ /// previously returned from [`create_inbound_payment`].
+ ///
+ /// [`create_inbound_payment`]: Self::create_inbound_payment
+ pub fn get_payment_preimage(&self, payment_hash: PaymentHash, payment_secret: PaymentSecret) -> Result<PaymentPreimage, APIError> {
+ inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
+ }
+
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
let events = core::cell::RefCell::new(Vec::new());
inbound_payment.expiry_time > header.time as u64
});
- let mut pending_events = self.pending_events.lock().unwrap();
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+ let mut pending_events = self.pending_events.lock().unwrap();
outbounds.retain(|payment_id, payment| {
if payment.remaining_parts() != 0 { return true }
if let PendingOutboundPayment::Retryable { starting_block_height, payment_hash, .. } = payment {
impl Writeable for ClaimableHTLC {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
let payment_data = match &self.onion_payload {
- OnionPayload::Invoice(data) => Some(data.clone()),
+ OnionPayload::Invoice { _legacy_hop_data } => Some(_legacy_hop_data),
_ => None,
};
let keysend_preimage = match self.onion_payload {
- OnionPayload::Invoice(_) => None,
+ OnionPayload::Invoice { .. } => None,
OnionPayload::Spontaneous(preimage) => Some(preimage.clone()),
};
- write_tlv_fields!
- (writer,
- {
- (0, self.prev_hop, required), (2, self.value, required),
- (4, payment_data, option), (6, self.cltv_expiry, required),
- (8, keysend_preimage, option),
- });
+ write_tlv_fields!(writer, {
+ (0, self.prev_hop, required),
+ (1, self.total_msat, required),
+ (2, self.value, required),
+ (4, payment_data, option),
+ (6, self.cltv_expiry, required),
+ (8, keysend_preimage, option),
+ });
Ok(())
}
}
let mut value = 0;
let mut payment_data: Option<msgs::FinalOnionHopData> = None;
let mut cltv_expiry = 0;
+ let mut total_msat = None;
let mut keysend_preimage: Option<PaymentPreimage> = None;
- read_tlv_fields!
- (reader,
- {
- (0, prev_hop, required), (2, value, required),
- (4, payment_data, option), (6, cltv_expiry, required),
- (8, keysend_preimage, option)
- });
+ read_tlv_fields!(reader, {
+ (0, prev_hop, required),
+ (1, total_msat, option),
+ (2, value, required),
+ (4, payment_data, option),
+ (6, cltv_expiry, required),
+ (8, keysend_preimage, option)
+ });
let onion_payload = match keysend_preimage {
Some(p) => {
if payment_data.is_some() {
return Err(DecodeError::InvalidValue)
}
+ if total_msat.is_none() {
+ total_msat = Some(value);
+ }
OnionPayload::Spontaneous(p)
},
None => {
if payment_data.is_none() {
return Err(DecodeError::InvalidValue)
}
- OnionPayload::Invoice(payment_data.unwrap())
+ if total_msat.is_none() {
+ total_msat = Some(payment_data.as_ref().unwrap().total_msat);
+ }
+ OnionPayload::Invoice { _legacy_hop_data: payment_data.unwrap() }
},
};
Ok(Self {
prev_hop: prev_hop.0.unwrap(),
value,
+ total_msat: total_msat.unwrap(),
onion_payload,
cltv_expiry,
})
let mut path = Some(Vec::new());
let mut payment_id = None;
let mut payment_secret = None;
- let mut payee = None;
+ let mut payment_params = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(3, payment_secret, option),
(4, path, vec_type),
- (5, payee, option),
+ (5, payment_params, option),
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
path: path.unwrap(),
payment_id: payment_id.unwrap(),
payment_secret,
- payee,
+ payment_params,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::io::Error> {
match self {
- HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, payment_secret, payee } => {
+ HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, payment_secret, payment_params } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
(2, first_hop_htlc_msat, required),
(3, payment_secret, option),
(4, path, vec_type),
- (5, payee, option),
+ (5, payment_params, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
peer_state.latest_features.write(writer)?;
}
+ let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
+ let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
let events = self.pending_events.lock().unwrap();
(events.len() as u64).write(writer)?;
for event in events.iter() {
(self.last_node_announcement_serial.load(Ordering::Acquire) as u32).write(writer)?;
(self.highest_seen_timestamp.load(Ordering::Acquire) as u32).write(writer)?;
- let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
(pending_inbound_payments.len() as u64).write(writer)?;
for (hash, pending_payment) in pending_inbound_payments.iter() {
hash.write(writer)?;
pending_payment.write(writer)?;
}
- let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
// For backwards compat, write the session privs and their total length.
let mut num_pending_outbounds_compat: u64 = 0;
for (_, outbound) in pending_outbound_payments.iter() {
write_tlv_fields!(writer, {
(1, pending_outbound_payments_no_retry, required),
(3, pending_outbound_payments, required),
+ (5, self.our_network_pubkey, required)
});
Ok(())
log_error!(args.logger, " The chain::Watch API *requires* that monitors are persisted durably before returning,");
log_error!(args.logger, " client applications must ensure that ChannelMonitor data is always available and the latest to avoid funds loss!");
log_error!(args.logger, " Without the latest ChannelMonitor we cannot continue without risking funds.");
- log_error!(args.logger, " Please ensure the chain::Watch API requirements are met and file a bug report at https://github.com/rust-bitcoin/rust-lightning");
+ log_error!(args.logger, " Please ensure the chain::Watch API requirements are met and file a bug report at https://github.com/lightningdevkit/rust-lightning");
return Err(DecodeError::InvalidValue);
} else if channel.get_cur_holder_commitment_transaction_number() > monitor.get_cur_holder_commitment_number() ||
channel.get_revoked_counterparty_commitment_transaction_number() > monitor.get_min_seen_secret() ||
log_error!(args.logger, " The chain::Watch API *requires* that monitors are persisted durably before returning,");
log_error!(args.logger, " client applications must ensure that ChannelMonitor data is always available and the latest to avoid funds loss!");
log_error!(args.logger, " Without the ChannelMonitor we cannot continue without risking funds.");
- log_error!(args.logger, " Please ensure the chain::Watch API requirements are met and file a bug report at https://github.com/rust-bitcoin/rust-lightning");
+ log_error!(args.logger, " Please ensure the chain::Watch API requirements are met and file a bug report at https://github.com/lightningdevkit/rust-lightning");
return Err(DecodeError::InvalidValue);
}
}
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
let mut pending_outbound_payments = None;
+ let mut received_network_pubkey: Option<PublicKey> = None;
read_tlv_fields!(reader, {
(1, pending_outbound_payments_no_retry, option),
(3, pending_outbound_payments, option),
+ (5, received_network_pubkey, option)
});
+
if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
pending_outbound_payments = Some(pending_outbound_payments_compat);
} else if pending_outbound_payments.is_none() {
pending_events_read.append(&mut channel_closures);
}
+ let our_network_pubkey = PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret());
+ if let Some(network_pubkey) = received_network_pubkey {
+ if network_pubkey != our_network_pubkey {
+ log_error!(args.logger, "Key that was generated does not match the existing key.");
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
let channel_manager = ChannelManager {
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
our_network_key: args.keys_manager.get_node_secret(),
- our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret()),
+ our_network_pubkey,
secp_ctx,
last_node_announcement_serial: AtomicUsize::new(last_node_announcement_serial as usize),
use ln::functional_test_utils::*;
use ln::msgs;
use ln::msgs::ChannelMessageHandler;
- use routing::router::{Payee, RouteParameters, find_route};
+ use routing::router::{PaymentParameters, RouteParameters, find_route};
use util::errors::APIError;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use util::test_utils;
// Use the utility function send_payment_along_path to send the payment with MPP data which
// indicates there are more HTLCs coming.
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.
- 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();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
expect_payment_failed!(nodes[0], our_payment_hash, true);
// Send the second half of the original MPP payment.
- 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();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &expected_route, 100_000);
// Next, attempt a keysend payment and make sure it fails.
- let params = RouteParameters {
- payee: Payee::for_keysend(expected_route.last().unwrap().node.get_our_node_id()),
+ let route_params = RouteParameters {
+ payment_params: PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id()),
final_value_msat: 100_000,
final_cltv_expiry_delta: TEST_FINAL_CLTV,
};
let route = find_route(
- &nodes[0].node.get_our_node_id(), ¶ms, nodes[0].network_graph, None,
+ &nodes[0].node.get_our_node_id(), &route_params, nodes[0].network_graph, None,
nodes[0].logger, &scorer
).unwrap();
nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
// To start (2), send a keysend payment but don't claim it.
let payment_preimage = PaymentPreimage([42; 32]);
let route = find_route(
- &nodes[0].node.get_our_node_id(), ¶ms, nodes[0].network_graph, None,
+ &nodes[0].node.get_our_node_id(), &route_params, nodes[0].network_graph, None,
nodes[0].logger, &scorer
).unwrap();
let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
- let params = RouteParameters {
- payee: Payee::for_keysend(payee_pubkey),
+ let route_params = RouteParameters {
+ payment_params: PaymentParameters::for_keysend(payee_pubkey),
final_value_msat: 10000,
final_cltv_expiry_delta: 40,
};
let first_hops = nodes[0].node.list_usable_channels();
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
- &payer_pubkey, ¶ms, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
+ &payer_pubkey, &route_params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer
).unwrap();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
- let params = RouteParameters {
- payee: Payee::for_keysend(payee_pubkey),
+ let route_params = RouteParameters {
+ payment_params: PaymentParameters::for_keysend(payee_pubkey),
final_value_msat: 10000,
final_cltv_expiry_delta: 40,
};
let first_hops = nodes[0].node.list_usable_channels();
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
- &payer_pubkey, ¶ms, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
+ &payer_pubkey, &route_params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer
).unwrap();
// payment verification fails as expected.
let mut bad_payment_hash = payment_hash.clone();
bad_payment_hash.0[0] += 1;
- match inbound_payment::verify(bad_payment_hash, payment_data.clone(), nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger) {
+ match inbound_payment::verify(bad_payment_hash, &payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger) {
Ok(_) => panic!("Unexpected ok"),
Err(()) => {
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager::inbound_payment".to_string(), "Failing HTLC with user-generated payment_hash".to_string(), 1);
}
// Check that using the original payment hash succeeds.
- assert!(inbound_payment::verify(payment_hash, payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger).is_ok());
+ assert!(inbound_payment::verify(payment_hash, &payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger).is_ok());
}
}
use ln::functional_test_utils::*;
use ln::msgs::{ChannelMessageHandler, Init};
use routing::network_graph::NetworkGraph;
- use routing::router::{Payee, get_route};
+ use routing::router::{PaymentParameters, get_route};
use routing::scoring::Scorer;
use util::test_utils;
use util::config::UserConfig;
macro_rules! send_payment {
($node_a: expr, $node_b: expr) => {
let usable_channels = $node_a.list_usable_channels();
- let payee = Payee::from_node_id($node_b.get_our_node_id())
+ let payment_params = PaymentParameters::from_node_id($node_b.get_our_node_id())
.with_features(InvoiceFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
- let route = get_route(&$node_a.get_our_node_id(), &payee, &dummy_graph,
+ let route = get_route(&$node_a.get_our_node_id(), &payment_params, &dummy_graph,
Some(&usable_channels.iter().map(|r| r).collect::<Vec<_>>()), 10_000, TEST_FINAL_CLTV, &logger_a, &scorer).unwrap();
let mut payment_preimage = PaymentPreimage([0; 32]);