use crate::onion_message;
use crate::sign::{NodeSigner, Recipient};
+#[allow(unused_imports)]
use crate::prelude::*;
-#[cfg(feature = "std")]
-use core::convert::TryFrom;
+
use core::fmt;
use core::fmt::Debug;
use core::ops::Deref;
Io(io::ErrorKind),
/// The message included zlib-compressed values, which we don't support.
UnsupportedCompression,
+ /// Value is validly encoded but is dangerous to use.
+ ///
+ /// This is used for things like [`ChannelManager`] deserialization where we want to ensure
+ /// that we don't use a [`ChannelManager`] which is in out of sync with the [`ChannelMonitor`].
+ /// This indicates that there is a critical implementation flaw in the storage implementation
+ /// and it's unsafe to continue.
+ ///
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+ DangerousValue,
}
/// An [`init`] message to be sent to or received from a peer.
pub short_channel_id_alias: Option<u64>,
}
+/// A randomly chosen number that is used to identify inputs within an interactive transaction
+/// construction.
+pub type SerialId = u64;
+
/// An stfu (quiescence) message to be sent by or received from the stfu initiator.
// TODO(splicing): Add spec link for `stfu`; still in draft, using from https://github.com/lightning/bolts/pull/863
#[derive(Clone, Debug, PartialEq, Eq)]
pub channel_id: ChannelId,
/// A randomly chosen unique identifier for this input, which is even for initiators and odd for
/// non-initiators.
- pub serial_id: u64,
+ pub serial_id: SerialId,
/// Serialized transaction that contains the output this input spends to verify that it is non
/// malleable.
pub prevtx: TransactionU16LenLimited,
pub channel_id: ChannelId,
/// A randomly chosen unique identifier for this output, which is even for initiators and odd for
/// non-initiators.
- pub serial_id: u64,
+ pub serial_id: SerialId,
/// The satoshi value of the output
pub sats: u64,
/// The scriptPubKey for the output
/// The channel ID
pub channel_id: ChannelId,
/// The serial ID of the input to be removed
- pub serial_id: u64,
+ pub serial_id: SerialId,
}
/// A tx_remove_output message for removing an output during interactive transaction construction.
/// The channel ID
pub channel_id: ChannelId,
/// The serial ID of the output to be removed
- pub serial_id: u64,
+ pub serial_id: SerialId,
}
/// A tx_complete message signalling the conclusion of a peer's transaction contributions during
pub alias: NodeAlias,
/// List of addresses on which this node is reachable
pub addresses: Vec<SocketAddress>,
- pub(crate) excess_address_data: Vec<u8>,
- pub(crate) excess_data: Vec<u8>,
+ /// Excess address data which was signed as a part of the message which we do not (yet) understand how
+ /// to decode.
+ ///
+ /// This is stored to ensure forward-compatibility as new address types are added to the lightning gossip protocol.
+ pub excess_address_data: Vec<u8>,
+ /// Excess data which was signed as a part of the message which we do not (yet) understand how
+ /// to decode.
+ ///
+ /// This is stored to ensure forward-compatibility as new fields are added to the lightning gossip protocol.
+ pub excess_data: Vec<u8>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
/// A [`node_announcement`] message to be sent to or received from a peer.
mod fuzzy_internal_msgs {
use bitcoin::secp256k1::PublicKey;
use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay};
- use crate::prelude::*;
use crate::ln::{PaymentPreimage, PaymentSecret};
use crate::ln::features::BlindedHopFeatures;
- use super::FinalOnionHopData;
+ use super::{FinalOnionHopData, TrampolineOnionPacket};
+
+ #[allow(unused_imports)]
+ use crate::prelude::*;
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
// them from untrusted input):
payment_secret: PaymentSecret,
payment_constraints: PaymentConstraints,
intro_node_blinding_point: Option<PublicKey>,
+ keysend_preimage: Option<PaymentPreimage>,
+ custom_tlvs: Vec<(u64, Vec<u8>)>,
}
}
amt_to_forward: u64,
outgoing_cltv_value: u32,
},
+ #[allow(unused)]
+ TrampolineEntrypoint {
+ amt_to_forward: u64,
+ outgoing_cltv_value: u32,
+ multipath_trampoline_data: Option<FinalOnionHopData>,
+ trampoline_packet: TrampolineOnionPacket,
+ },
Receive {
payment_data: Option<FinalOnionHopData>,
payment_metadata: Option<Vec<u8>>,
cltv_expiry_height: u32,
encrypted_tlvs: Vec<u8>,
intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
+ keysend_preimage: Option<PaymentPreimage>,
+ custom_tlvs: Vec<(u64, Vec<u8>)>,
+ }
+ }
+
+ pub(crate) enum OutboundTrampolinePayload {
+ #[allow(unused)]
+ Forward {
+ /// The value, in msat, of the payment after this hop's fee is deducted.
+ amt_to_forward: u64,
+ outgoing_cltv_value: u32,
+ /// The node id to which the trampoline node must find a route
+ outgoing_node_id: PublicKey,
}
}
}
}
+/// BOLT 4 onion packet including hop data for the next peer.
+#[derive(Clone, Hash, PartialEq, Eq)]
+pub struct TrampolineOnionPacket {
+ /// Bolt 04 version number
+ pub version: u8,
+ /// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data
+ pub public_key: PublicKey,
+ /// Encrypted payload for the next hop
+ //
+ // Unlike the onion packets used for payments, Trampoline onion packets have to be shorter than
+ // 1300 bytes. The expected default is 650 bytes.
+ // TODO: if 650 ends up being the most common size, optimize this to be:
+ // enum { SixFifty([u8; 650]), VarLen(Vec<u8>) }
+ pub hop_data: Vec<u8>,
+ /// HMAC to verify the integrity of hop_data
+ pub hmac: [u8; 32],
+}
+
+impl onion_utils::Packet for TrampolineOnionPacket {
+ type Data = Vec<u8>;
+ fn new(public_key: PublicKey, hop_data: Vec<u8>, hmac: [u8; 32]) -> Self {
+ Self {
+ version: 0,
+ public_key,
+ hop_data,
+ hmac,
+ }
+ }
+}
+
+impl Writeable for TrampolineOnionPacket {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.version.write(w)?;
+ self.public_key.write(w)?;
+ w.write_all(&self.hop_data)?;
+ self.hmac.write(w)?;
+ Ok(())
+ }
+}
+
+impl Debug for TrampolineOnionPacket {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_fmt(format_args!("TrampolineOnionPacket version {} with hmac {:?}", self.version, &self.hmac[..]))
+ }
+}
+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub(crate) struct OnionErrorPacket {
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
DecodeError::BadLengthDescriptor => f.write_str("A length descriptor in the packet didn't describe the later data correctly"),
DecodeError::Io(ref e) => fmt::Debug::fmt(e, f),
DecodeError::UnsupportedCompression => f.write_str("We don't support receiving messages with zlib-compressed fields"),
+ DecodeError::DangerousValue => f.write_str("Value would be dangerous to continue execution with"),
}
}
}
(6, short_channel_id, required)
});
},
+ Self::TrampolineEntrypoint {
+ amt_to_forward, outgoing_cltv_value, ref multipath_trampoline_data,
+ ref trampoline_packet
+ } => {
+ _encode_varint_length_prefixed_tlv!(w, {
+ (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+ (8, multipath_trampoline_data, option),
+ (20, trampoline_packet, required)
+ });
+ },
Self::Receive {
ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat,
cltv_expiry_height, ref custom_tlvs,
},
Self::BlindedReceive {
sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs,
- intro_node_blinding_point,
+ intro_node_blinding_point, keysend_preimage, ref custom_tlvs,
} => {
+ // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
+ // to reject any reserved types in the experimental range if new ones are ever
+ // standardized.
+ let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode()));
+ let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect();
+ custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
_encode_varint_length_prefixed_tlv!(w, {
(2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),
(4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required),
(10, *encrypted_tlvs, required_vec),
(12, intro_node_blinding_point, option),
(18, HighZeroBytesDroppedBigSize(*total_msat), required)
- });
+ }, custom_tlvs.iter());
},
}
Ok(())
}
}
+impl Writeable for OutboundTrampolinePayload {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ match self {
+ Self::Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => {
+ _encode_varint_length_prefixed_tlv!(w, {
+ (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+ (14, outgoing_node_id, required)
+ });
+ }
+ }
+ Ok(())
+ }
+}
+
+
impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload where NS::Target: NodeSigner {
fn read<R: Read>(r: &mut R, args: (Option<PublicKey>, &NS)) -> Result<Self, DecodeError> {
let (update_add_blinding_point, node_signer) = args;
}
if let Some(blinding_point) = intro_node_blinding_point.or(update_add_blinding_point) {
- if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() ||
- keysend_preimage.is_some()
- {
+ if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() {
return Err(DecodeError::InvalidValue)
}
let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0;
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Forward(ForwardTlvs {
short_channel_id, payment_relay, payment_constraints, features
})} => {
- if amt.is_some() || cltv_value.is_some() || total_msat.is_some() {
+ if amt.is_some() || cltv_value.is_some() || total_msat.is_some() ||
+ keysend_preimage.is_some()
+ {
return Err(DecodeError::InvalidValue)
}
Ok(Self::BlindedForward {
payment_secret,
payment_constraints,
intro_node_blinding_point,
+ keysend_preimage,
+ custom_tlvs,
})
},
}
#[cfg(test)]
mod tests {
- use std::convert::TryFrom;
use bitcoin::{Transaction, TxIn, ScriptBuf, Sequence, Witness, TxOut};
use hex::DisplayHex;
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::ChannelId;
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
- use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields};
+ use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, TrampolineOnionPacket};
use crate::ln::msgs::SocketAddress;
use crate::routing::gossip::{NodeAlias, NodeId};
- use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited};
+ use crate::util::ser::{BigSize, Hostname, Readable, ReadableArgs, TransactionU16LenLimited, Writeable};
use crate::util::test_utils;
use bitcoin::hashes::hex::FromHex;
} else { panic!(); }
}
+ #[test]
+ fn encoding_final_onion_hop_data_with_trampoline_packet() {
+ let secp_ctx = Secp256k1::new();
+ let (_private_key, public_key) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+
+ let compressed_public_key = public_key.serialize();
+ assert_eq!(compressed_public_key.len(), 33);
+
+ let trampoline_packet = TrampolineOnionPacket {
+ version: 0,
+ public_key,
+ hop_data: vec![1; 650], // this should be the standard encoded length
+ hmac: [2; 32],
+ };
+ let encoded_trampoline_packet = trampoline_packet.encode();
+ assert_eq!(encoded_trampoline_packet.len(), 716);
+
+ let msg = msgs::OutboundOnionPayload::TrampolineEntrypoint {
+ multipath_trampoline_data: None,
+ amt_to_forward: 0x0badf00d01020304,
+ outgoing_cltv_value: 0xffffffff,
+ trampoline_packet,
+ };
+ let encoded_payload = msg.encode();
+
+ let trampoline_type_bytes = &encoded_payload[19..=19];
+ let mut trampoline_type_cursor = Cursor::new(trampoline_type_bytes);
+ let trampoline_type_big_size: BigSize = Readable::read(&mut trampoline_type_cursor).unwrap();
+ assert_eq!(trampoline_type_big_size.0, 20);
+
+ let trampoline_length_bytes = &encoded_payload[20..=22];
+ let mut trampoline_length_cursor = Cursor::new(trampoline_length_bytes);
+ let trampoline_length_big_size: BigSize = Readable::read(&mut trampoline_length_cursor).unwrap();
+ assert_eq!(trampoline_length_big_size.0, encoded_trampoline_packet.len() as u64);
+ }
+
+ #[test]
+ fn encoding_final_onion_hop_data_with_eclair_trampoline_packet() {
+ let public_key = PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap();
+ let hop_data = <Vec<u8>>::from_hex("cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4c").unwrap();
+ let hmac_vector = <Vec<u8>>::from_hex("bb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
+ let mut hmac = [0; 32];
+ hmac.copy_from_slice(&hmac_vector);
+
+ let compressed_public_key = public_key.serialize();
+ assert_eq!(compressed_public_key.len(), 33);
+
+ let trampoline_packet = TrampolineOnionPacket {
+ version: 0,
+ public_key,
+ hop_data,
+ hmac,
+ };
+ let encoded_trampoline_packet = trampoline_packet.encode();
+ let expected_eclair_trampoline_packet = <Vec<u8>>::from_hex("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4cbb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
+ assert_eq!(encoded_trampoline_packet, expected_eclair_trampoline_packet);
+ }
+
#[test]
fn query_channel_range_end_blocknum() {
let tests: Vec<(u32, u32, u32)> = vec![