|  | 
|  | 1 | +//! Data structures and methods for constructing [`BlindedPath`]s to send a payment over. | 
|  | 2 | +//! | 
|  | 3 | +//! [`BlindedPath`]: crate::blinded_path::BlindedPath | 
|  | 4 | +
 | 
|  | 5 | +use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; | 
|  | 6 | + | 
|  | 7 | +use crate::blinded_path::BlindedHop; | 
|  | 8 | +use crate::blinded_path::utils; | 
|  | 9 | +use crate::io; | 
|  | 10 | +use crate::ln::PaymentSecret; | 
|  | 11 | +use crate::ln::features::BlindedHopFeatures; | 
|  | 12 | +use crate::ln::msgs::DecodeError; | 
|  | 13 | +use crate::prelude::*; | 
|  | 14 | +use crate::util::ser::{Readable, Writeable, Writer}; | 
|  | 15 | + | 
|  | 16 | +/// Data to construct a [`BlindedHop`] for forwarding a payment. | 
|  | 17 | +pub struct ForwardTlvs { | 
|  | 18 | +	/// The short channel id this payment should be forwarded out over. | 
|  | 19 | +	short_channel_id: u64, | 
|  | 20 | +	/// Payment parameters for relaying over [`Self::short_channel_id`]. | 
|  | 21 | +	payment_relay: PaymentRelay, | 
|  | 22 | +	/// Payment constraints for relaying over [`Self::short_channel_id`]. | 
|  | 23 | +	payment_constraints: PaymentConstraints, | 
|  | 24 | +	/// Supported and required features when relaying a payment onion containing this object's | 
|  | 25 | +	/// corresponding [`BlindedHop::encrypted_payload`]. | 
|  | 26 | +	/// | 
|  | 27 | +	/// [`BlindedHop::encrypted_payload`]: crate::blinded_path::BlindedHop::encrypted_payload | 
|  | 28 | +	features: BlindedHopFeatures, | 
|  | 29 | +} | 
|  | 30 | + | 
|  | 31 | +/// Data to construct a [`BlindedHop`] for receiving a payment. This payload is custom to LDK and | 
|  | 32 | +/// may not be valid if received by another lightning implementation. | 
|  | 33 | +pub struct ReceiveTlvs { | 
|  | 34 | +	/// Used to authenticate the sender of a payment to the receiver and tie MPP HTLCs together. | 
|  | 35 | +	payment_secret: PaymentSecret, | 
|  | 36 | +	/// Constraints for the receiver of this payment. | 
|  | 37 | +	payment_constraints: PaymentConstraints, | 
|  | 38 | +} | 
|  | 39 | + | 
|  | 40 | +/// Data to construct a [`BlindedHop`] for sending a payment over. | 
|  | 41 | +/// | 
|  | 42 | +/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 43 | +pub(crate) enum BlindedPaymentTlvs { | 
|  | 44 | +	/// This blinded payment data is for a forwarding node. | 
|  | 45 | +	Forward(ForwardTlvs), | 
|  | 46 | +	/// This blinded payment data is for the receiving node. | 
|  | 47 | +	Receive(ReceiveTlvs), | 
|  | 48 | +} | 
|  | 49 | + | 
|  | 50 | +// Used to include forward and receive TLVs in the same iterator for encoding. | 
|  | 51 | +enum BlindedPaymentTlvsRef<'a> { | 
|  | 52 | +	Forward(&'a ForwardTlvs), | 
|  | 53 | +	Receive(&'a ReceiveTlvs), | 
|  | 54 | +} | 
|  | 55 | + | 
|  | 56 | +/// Parameters for relaying over a given [`BlindedHop`]. | 
|  | 57 | +/// | 
|  | 58 | +/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 59 | +pub struct PaymentRelay { | 
|  | 60 | +	/// Number of blocks subtracted from an incoming HTLC's `cltv_expiry` for this [`BlindedHop`]. | 
|  | 61 | +	/// | 
|  | 62 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 63 | +	pub cltv_expiry_delta: u16, | 
|  | 64 | +	/// Liquidity fee charged (in millionths of the amount transferred) for relaying a payment over | 
|  | 65 | +	/// this [`BlindedHop`], (i.e., 10,000 is 1%). | 
|  | 66 | +	/// | 
|  | 67 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 68 | +	pub fee_proportional_millionths: u32, | 
|  | 69 | +	/// Base fee charged (in millisatoshi) for relaying a payment over this [`BlindedHop`]. | 
|  | 70 | +	/// | 
|  | 71 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 72 | +	pub fee_base_msat: u32, | 
|  | 73 | +} | 
|  | 74 | + | 
|  | 75 | +/// Constraints for relaying over a given [`BlindedHop`]. | 
|  | 76 | +/// | 
|  | 77 | +/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 78 | +pub struct PaymentConstraints { | 
|  | 79 | +	/// The maximum total CLTV delta that is acceptable when relaying a payment over this | 
|  | 80 | +	/// [`BlindedHop`]. | 
|  | 81 | +	/// | 
|  | 82 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 83 | +	pub max_cltv_expiry: u32, | 
|  | 84 | +	/// The minimum value, in msat, that may be relayed over this [`BlindedHop`]. | 
|  | 85 | +	pub htlc_minimum_msat: u64, | 
|  | 86 | +} | 
|  | 87 | + | 
|  | 88 | +impl_writeable_tlv_based!(ForwardTlvs, { | 
|  | 89 | +	(2, short_channel_id, required), | 
|  | 90 | +	(10, payment_relay, required), | 
|  | 91 | +	(12, payment_constraints, required), | 
|  | 92 | +	(14, features, required), | 
|  | 93 | +}); | 
|  | 94 | + | 
|  | 95 | +impl_writeable_tlv_based!(ReceiveTlvs, { | 
|  | 96 | +	(12, payment_constraints, required), | 
|  | 97 | +	(65536, payment_secret, required), | 
|  | 98 | +}); | 
|  | 99 | + | 
|  | 100 | +impl<'a> Writeable for BlindedPaymentTlvsRef<'a> { | 
|  | 101 | +	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { | 
|  | 102 | +		// TODO: write padding | 
|  | 103 | +		match self { | 
|  | 104 | +			Self::Forward(tlvs) => tlvs.write(w)?, | 
|  | 105 | +			Self::Receive(tlvs) => tlvs.write(w)?, | 
|  | 106 | +		} | 
|  | 107 | +		Ok(()) | 
|  | 108 | +	} | 
|  | 109 | +} | 
|  | 110 | + | 
|  | 111 | +impl Readable for BlindedPaymentTlvs { | 
|  | 112 | +	fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> { | 
|  | 113 | +		_init_and_read_tlv_stream!(r, { | 
|  | 114 | +			(1, _padding, option), | 
|  | 115 | +			(2, scid, option), | 
|  | 116 | +			(10, payment_relay, option), | 
|  | 117 | +			(12, payment_constraints, required), | 
|  | 118 | +			(14, features, option), | 
|  | 119 | +			(65536, payment_secret, option), | 
|  | 120 | +		}); | 
|  | 121 | +		if let Some(short_channel_id) = scid { | 
|  | 122 | +			if payment_secret.is_some() { return Err(DecodeError::InvalidValue) } | 
|  | 123 | +			Ok(BlindedPaymentTlvs::Forward(ForwardTlvs { | 
|  | 124 | +				short_channel_id, | 
|  | 125 | +				payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?, | 
|  | 126 | +				payment_constraints: payment_constraints.0.unwrap(), | 
|  | 127 | +				features: features.ok_or(DecodeError::InvalidValue)?, | 
|  | 128 | +			})) | 
|  | 129 | +		} else { | 
|  | 130 | +			if payment_relay.is_some() || features.is_some() { return Err(DecodeError::InvalidValue) } | 
|  | 131 | +			Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs { | 
|  | 132 | +				payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, | 
|  | 133 | +				payment_constraints: payment_constraints.0.unwrap(), | 
|  | 134 | +			})) | 
|  | 135 | +		} | 
|  | 136 | +	} | 
|  | 137 | +} | 
|  | 138 | + | 
|  | 139 | +/// Construct blinded payment hops for the given `intermediate_nodes` and payee info. | 
|  | 140 | +pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>( | 
|  | 141 | +	secp_ctx: &Secp256k1<T>, intermediate_nodes: &[(PublicKey, ForwardTlvs)], | 
|  | 142 | +	payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, session_priv: &SecretKey | 
|  | 143 | +) -> Result<Vec<BlindedHop>, secp256k1::Error> { | 
|  | 144 | +	let pks = intermediate_nodes.iter().map(|(pk, _)| pk) | 
|  | 145 | +		.chain(core::iter::once(&payee_node_id)); | 
|  | 146 | +	let tlvs = intermediate_nodes.iter().map(|(_, tlvs)| BlindedPaymentTlvsRef::Forward(tlvs)) | 
|  | 147 | +		.chain(core::iter::once(BlindedPaymentTlvsRef::Receive(&payee_tlvs))); | 
|  | 148 | +	utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv) | 
|  | 149 | +} | 
|  | 150 | + | 
|  | 151 | +impl_writeable_msg!(PaymentRelay, { | 
|  | 152 | +	cltv_expiry_delta, | 
|  | 153 | +	fee_proportional_millionths, | 
|  | 154 | +	fee_base_msat | 
|  | 155 | +}, {}); | 
|  | 156 | + | 
|  | 157 | +impl_writeable_msg!(PaymentConstraints, { | 
|  | 158 | +	max_cltv_expiry, | 
|  | 159 | +	htlc_minimum_msat | 
|  | 160 | +}, {}); | 
0 commit comments