},
}
- pub(crate) enum OnionHopDataFormat {
- NonFinalNode {
+ pub(crate) enum OutboundOnionPayload {
+ Forward {
short_channel_id: u64,
+ /// The value, in msat, of the payment after this hop's fee is deducted.
+ amt_to_forward: u64,
+ outgoing_cltv_value: u32,
},
- FinalNode {
+ Receive {
payment_data: Option<FinalOnionHopData>,
payment_metadata: Option<Vec<u8>>,
keysend_preimage: Option<PaymentPreimage>,
+ amt_msat: u64,
+ outgoing_cltv_value: u32,
},
}
- pub struct OnionHopData {
- pub(crate) format: OnionHopDataFormat,
- /// The value, in msat, of the payment after this hop's fee is deducted.
- pub(crate) amt_to_forward: u64,
- pub(crate) outgoing_cltv_value: u32,
- }
-
pub struct DecodedOnionErrorPacket {
pub(crate) hmac: [u8; 32],
pub(crate) failuremsg: Vec<u8>,
}
}
-impl Writeable for OnionHopData {
+impl Writeable for OutboundOnionPayload {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- match self.format {
- OnionHopDataFormat::NonFinalNode { short_channel_id } => {
+ match self {
+ Self::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } => {
_encode_varint_length_prefixed_tlv!(w, {
- (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
- (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
+ (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
(6, short_channel_id, required)
});
},
- OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage } => {
+ Self::Receive {
+ ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat, outgoing_cltv_value
+ } => {
_encode_varint_length_prefixed_tlv!(w, {
- (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
- (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
+ (2, HighZeroBytesDroppedBigSize(*amt_msat), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
(8, payment_data, option),
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
(5482373484, keysend_preimage, option)
use hex;
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
- use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, OnionHopDataFormat};
+ use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket};
use crate::routing::gossip::{NodeAlias, NodeId};
use crate::util::ser::{Writeable, Readable, Hostname, TransactionU16LenLimited};
#[test]
fn encoding_nonfinal_onion_hop_data() {
- let msg = msgs::OnionHopData {
- format: OnionHopDataFormat::NonFinalNode {
- short_channel_id: 0xdeadbeef1bad1dea,
- },
+ let outbound_msg = msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 0xdeadbeef1bad1dea,
amt_to_forward: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
};
- let encoded_value = msg.encode();
+ let encoded_value = outbound_msg.encode();
let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap();
assert_eq!(encoded_value, target_value);
#[test]
fn encoding_final_onion_hop_data() {
- let msg = msgs::OnionHopData {
- format: OnionHopDataFormat::FinalNode {
- payment_data: None,
- payment_metadata: None,
- keysend_preimage: None,
- },
- amt_to_forward: 0x0badf00d01020304,
+ let outbound_msg = msgs::OutboundOnionPayload::Receive {
+ payment_data: None,
+ payment_metadata: None,
+ keysend_preimage: None,
+ amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
};
- let encoded_value = msg.encode();
+ let encoded_value = outbound_msg.encode();
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
assert_eq!(encoded_value, target_value);
#[test]
fn encoding_final_onion_hop_data_with_secret() {
let expected_payment_secret = PaymentSecret([0x42u8; 32]);
- let msg = msgs::OnionHopData {
- format: OnionHopDataFormat::FinalNode {
- payment_data: Some(FinalOnionHopData {
- payment_secret: expected_payment_secret,
- total_msat: 0x1badca1f
- }),
- payment_metadata: None,
- keysend_preimage: None,
- },
- amt_to_forward: 0x0badf00d01020304,
+ let outbound_msg = msgs::OutboundOnionPayload::Receive {
+ payment_data: Some(FinalOnionHopData {
+ payment_secret: expected_payment_secret,
+ total_msat: 0x1badca1f
+ }),
+ payment_metadata: None,
+ keysend_preimage: None,
+ amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
};
- let encoded_value = msg.encode();
+ let encoded_value = outbound_msg.encode();
let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
assert_eq!(encoded_value, target_value);
// see above test, needs to be a separate method for use of the serialization macros.
fn encode_big_payload() -> Result<Vec<u8>, io::Error> {
use crate::util::ser::HighZeroBytesDroppedBigSize;
- let payload = msgs::OnionHopData {
- format: OnionHopDataFormat::NonFinalNode {
- short_channel_id: 0xdeadbeef1bad1dea,
- },
+ let payload = msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 0xdeadbeef1bad1dea,
amt_to_forward: 1000,
outgoing_cltv_value: 0xffffffff,
};
let mut encoded_payload = Vec::new();
let test_bytes = vec![42u8; 1000];
- if let OnionHopDataFormat::NonFinalNode { short_channel_id } = payload.format {
+ if let msgs::OutboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } = payload {
_encode_varint_length_prefixed_tlv!(&mut encoded_payload, {
(1, test_bytes, required_vec),
- (2, HighZeroBytesDroppedBigSize(payload.amt_to_forward), required),
- (4, HighZeroBytesDroppedBigSize(payload.outgoing_cltv_value), required),
+ (2, HighZeroBytesDroppedBigSize(amt_to_forward), required),
+ (4, HighZeroBytesDroppedBigSize(outgoing_cltv_value), required),
(6, short_channel_id, required)
});
}
}
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
-pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_onion: RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
+pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_onion: RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(Vec<msgs::OutboundOnionPayload>, u64, u32), APIError> {
let mut cur_value_msat = 0u64;
let mut cur_cltv = starting_htlc_offset;
let mut last_short_channel_id = 0;
- let mut res: Vec<msgs::OnionHopData> = Vec::with_capacity(path.hops.len());
+ let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(path.hops.len());
for (idx, hop) in path.hops.iter().rev().enumerate() {
// First hop gets special values so that it can check, on receipt, that everything is
// the intended recipient).
let value_msat = if cur_value_msat == 0 { hop.fee_msat } else { cur_value_msat };
let cltv = if cur_cltv == starting_htlc_offset { hop.cltv_expiry_delta + starting_htlc_offset } else { cur_cltv };
- res.insert(0, msgs::OnionHopData {
- format: if idx == 0 {
- msgs::OnionHopDataFormat::FinalNode {
- payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
- Some(msgs::FinalOnionHopData {
- payment_secret: secret,
- total_msat,
- })
- } else { None },
- payment_metadata: recipient_onion.payment_metadata.take(),
- keysend_preimage: *keysend_preimage,
- }
- } else {
- msgs::OnionHopDataFormat::NonFinalNode {
- short_channel_id: last_short_channel_id,
- }
- },
- amt_to_forward: value_msat,
- outgoing_cltv_value: cltv,
+ res.insert(0, if idx == 0 {
+ msgs::OutboundOnionPayload::Receive {
+ payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
+ Some(msgs::FinalOnionHopData {
+ payment_secret: secret,
+ total_msat,
+ })
+ } else { None },
+ payment_metadata: recipient_onion.payment_metadata.take(),
+ keysend_preimage: *keysend_preimage,
+ amt_msat: value_msat,
+ outgoing_cltv_value: cltv,
+ }
+ } else {
+ msgs::OutboundOnionPayload::Forward {
+ short_channel_id: last_short_channel_id,
+ amt_to_forward: value_msat,
+ outgoing_cltv_value: cltv,
+ }
});
cur_value_msat += hop.fee_msat;
if cur_value_msat >= 21000000 * 100000000 * 1000 {
}
}
-pub(super) fn construct_onion_packet(payloads: Vec<msgs::OnionHopData>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], associated_data: &PaymentHash) -> Result<msgs::OnionPacket, ()> {
+pub(super) fn construct_onion_packet(
+ payloads: Vec<msgs::OutboundOnionPayload>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32],
+ associated_data: &PaymentHash
+) -> Result<msgs::OnionPacket, ()> {
let mut packet_data = [0; ONION_DATA_LEN];
let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
// with raw hex instead of our in-memory enums, as the payloads contains custom types, and
// we have no way of representing that with our enums.
let payloads = vec!(
- RawOnionHopData::new(msgs::OnionHopData {
- format: msgs::OnionHopDataFormat::NonFinalNode {
- short_channel_id: 1,
- },
+ RawOnionHopData::new(msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 1,
amt_to_forward: 15000,
outgoing_cltv_value: 1500,
}),
RawOnionHopData {
data: hex::decode("52020236b00402057806080000000000000002fd02013c0102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f").unwrap(),
},
- RawOnionHopData::new(msgs::OnionHopData {
- format: msgs::OnionHopDataFormat::NonFinalNode {
- short_channel_id: 3,
- },
+ RawOnionHopData::new(msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 3,
amt_to_forward: 12500,
outgoing_cltv_value: 1250,
}),
- RawOnionHopData::new(msgs::OnionHopData {
- format: msgs::OnionHopDataFormat::NonFinalNode {
- short_channel_id: 4,
- },
+ RawOnionHopData::new(msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 4,
amt_to_forward: 10000,
outgoing_cltv_value: 1000,
}),
data: Vec<u8>
}
impl RawOnionHopData {
- fn new(orig: msgs::OnionHopData) -> Self {
+ fn new(orig: msgs::OutboundOnionPayload) -> Self {
Self { data: orig.encode() }
}
}