|  | 
|  | 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 sending a payment over. | 
|  | 17 | +/// | 
|  | 18 | +/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 19 | +pub enum BlindedPaymentTlvs { | 
|  | 20 | +	/// This blinded payment data is to be forwarded. | 
|  | 21 | +	Forward { | 
|  | 22 | +		/// The short channel id this payment is being forwarded over. | 
|  | 23 | +		short_channel_id: u64, | 
|  | 24 | +		/// Payment parameters for relaying over this channel. | 
|  | 25 | +		payment_relay: PaymentRelay, | 
|  | 26 | +		/// Payment constraints when relaying over this channel. | 
|  | 27 | +		payment_constraints: PaymentConstraints, | 
|  | 28 | +		/// Supported and required features when relaying a payment onion containing this object's | 
|  | 29 | +		/// corresponding [`BlindedHop`]. | 
|  | 30 | +		/// | 
|  | 31 | +		/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 32 | +		features: BlindedHopFeatures, | 
|  | 33 | +	}, | 
|  | 34 | +	/// This blinded payment data is to be received. | 
|  | 35 | +	Receive { | 
|  | 36 | +		/// Used to authenticate the sender of a payment to the receiver and tie MPP HTLCs together. | 
|  | 37 | +		payment_secret: PaymentSecret, | 
|  | 38 | +		/// Constraints for the receiver of this payment. | 
|  | 39 | +		payment_constraints: PaymentConstraints, | 
|  | 40 | +		/// Supported and required features when receiving a payment containing this object's | 
|  | 41 | +		/// corresponding [`BlindedHop`]. | 
|  | 42 | +		/// | 
|  | 43 | +		/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 44 | +		features: BlindedHopFeatures, | 
|  | 45 | +	}, | 
|  | 46 | +} | 
|  | 47 | + | 
|  | 48 | +/// Parameters for relaying over a given [`BlindedHop`]. | 
|  | 49 | +/// | 
|  | 50 | +/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 51 | +pub struct PaymentRelay { | 
|  | 52 | +	/// Number of blocks subtracted from an incoming HTLC's `cltv_expiry` for this [`BlindedHop`]. | 
|  | 53 | +	/// | 
|  | 54 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 55 | +	pub cltv_expiry_delta: u16, | 
|  | 56 | +	/// Liquidity fee charged (in millionths of the amount transferred) for relaying a payment over | 
|  | 57 | +	/// this [`BlindedHop`], (i.e., 10,000 is 1%). | 
|  | 58 | +	/// | 
|  | 59 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 60 | +	pub fee_proportional_millionths: u32, | 
|  | 61 | +	/// Base fee charged (in millisatoshi) for relaying a payment over this [`BlindedHop`]. | 
|  | 62 | +	/// | 
|  | 63 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 64 | +	pub fee_base_msat: u32, | 
|  | 65 | +} | 
|  | 66 | + | 
|  | 67 | +/// Constraints for relaying over a given [`BlindedHop`]. | 
|  | 68 | +/// | 
|  | 69 | +/// [`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 70 | +pub struct PaymentConstraints { | 
|  | 71 | +	/// The maximum total CLTV delta that is acceptable when relaying a payment over this | 
|  | 72 | +	/// [`BlindedHop`]. | 
|  | 73 | +	/// | 
|  | 74 | +	///[`BlindedHop`]: crate::blinded_path::BlindedHop | 
|  | 75 | +	pub max_cltv_expiry: u32, | 
|  | 76 | +	/// The minimum value, in msat, that may be relayed over this [`BlindedHop`]. | 
|  | 77 | +	pub htlc_minimum_msat: u64, | 
|  | 78 | +} | 
|  | 79 | + | 
|  | 80 | +impl Writeable for BlindedPaymentTlvs { | 
|  | 81 | +	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { | 
|  | 82 | +		// TODO: write padding | 
|  | 83 | +		match self { | 
|  | 84 | +			Self::Forward { short_channel_id, payment_relay, payment_constraints, features } => { | 
|  | 85 | +				encode_tlv_stream!(w, { | 
|  | 86 | +					(2, short_channel_id, required), | 
|  | 87 | +					(10, payment_relay, required), | 
|  | 88 | +					(12, payment_constraints, required), | 
|  | 89 | +					(14, features, required), | 
|  | 90 | +				}); | 
|  | 91 | +			}, | 
|  | 92 | +			Self::Receive { payment_secret, payment_constraints, features } => { | 
|  | 93 | +				encode_tlv_stream!(w, { | 
|  | 94 | +					(6, payment_secret, required), | 
|  | 95 | +					(12, payment_constraints, required), | 
|  | 96 | +					(14, features, required), | 
|  | 97 | +				}); | 
|  | 98 | +			} | 
|  | 99 | +		} | 
|  | 100 | +		Ok(()) | 
|  | 101 | +	} | 
|  | 102 | +} | 
|  | 103 | + | 
|  | 104 | +impl Readable for BlindedPaymentTlvs { | 
|  | 105 | +	fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> { | 
|  | 106 | +		_init_and_decode_tlv_stream!(r, { | 
|  | 107 | +			(1, _padding, option), | 
|  | 108 | +			(2, scid, option), | 
|  | 109 | +			(6, payment_secret, option), | 
|  | 110 | +			(10, payment_relay, option), | 
|  | 111 | +			(12, payment_constraints, required), | 
|  | 112 | +			(14, features, required), | 
|  | 113 | +		}); | 
|  | 114 | +		if let Some(short_channel_id) = scid { | 
|  | 115 | +			if payment_secret.is_some() { return Err(DecodeError::InvalidValue) } | 
|  | 116 | +			Ok(BlindedPaymentTlvs::Forward { | 
|  | 117 | +				short_channel_id, | 
|  | 118 | +				payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?, | 
|  | 119 | +				payment_constraints: payment_constraints.0.unwrap(), | 
|  | 120 | +				features: features.0.unwrap(), | 
|  | 121 | +			}) | 
|  | 122 | +		} else { | 
|  | 123 | +			if payment_relay.is_some() { return Err(DecodeError::InvalidValue) } | 
|  | 124 | +			Ok(BlindedPaymentTlvs::Receive { | 
|  | 125 | +				payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, | 
|  | 126 | +				payment_constraints: payment_constraints.0.unwrap(), | 
|  | 127 | +				features: features.0.unwrap(), | 
|  | 128 | +			}) | 
|  | 129 | +		} | 
|  | 130 | +	} | 
|  | 131 | +} | 
|  | 132 | + | 
|  | 133 | +/// Construct blinded payment hops for the given `unblinded_path`. | 
|  | 134 | +pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>( | 
|  | 135 | +	secp_ctx: &Secp256k1<T>, unblinded_path: &[(PublicKey, BlindedPaymentTlvs)], session_priv: &SecretKey | 
|  | 136 | +) -> Result<Vec<BlindedHop>, secp256k1::Error> { | 
|  | 137 | +	let mut blinded_hops = Vec::with_capacity(unblinded_path.len()); | 
|  | 138 | +	let mut curr_hop_idx = 0; | 
|  | 139 | +	utils::construct_keys_callback( | 
|  | 140 | +		secp_ctx, unblinded_path.iter().map(|(pk, _)| pk), None, session_priv, | 
|  | 141 | +		|blinded_node_id, _, _, encrypted_payload_ss, _, _| { | 
|  | 142 | +			blinded_hops.push(BlindedHop { | 
|  | 143 | +				blinded_node_id, | 
|  | 144 | +				encrypted_payload: utils::encrypt_payload(&unblinded_path[curr_hop_idx].1, encrypted_payload_ss), | 
|  | 145 | +			}); | 
|  | 146 | +			curr_hop_idx += 1; | 
|  | 147 | +		})?; | 
|  | 148 | +	Ok(blinded_hops) | 
|  | 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