-/// Construct blinded onion message hops for the given `unblinded_path`.
-fn blinded_message_hops<T: secp256k1::Signing + secp256k1::Verification>(
- secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
-) -> Result<Vec<BlindedHop>, secp256k1::Error> {
- let mut blinded_hops = Vec::with_capacity(unblinded_path.len());
-
- let mut prev_ss_and_blinded_node_id = None;
- utils::construct_keys_callback(secp_ctx, unblinded_path, None, session_priv, |blinded_node_id, _, _, encrypted_payload_ss, unblinded_pk, _| {
- if let Some((prev_ss, prev_blinded_node_id)) = prev_ss_and_blinded_node_id {
- if let Some(pk) = unblinded_pk {
- let payload = ForwardTlvs {
- next_node_id: pk,
- next_blinding_override: None,
- };
- blinded_hops.push(BlindedHop {
- blinded_node_id: prev_blinded_node_id,
- encrypted_payload: encrypt_payload(payload, prev_ss),
- });
- } else { debug_assert!(false); }
- }
- prev_ss_and_blinded_node_id = Some((encrypted_payload_ss, blinded_node_id));
- })?;
-
- if let Some((final_ss, final_blinded_node_id)) = prev_ss_and_blinded_node_id {
- let final_payload = ReceiveTlvs { path_id: None };
- blinded_hops.push(BlindedHop {
- blinded_node_id: final_blinded_node_id,
- encrypted_payload: encrypt_payload(final_payload, final_ss),
- });
- } else { debug_assert!(false) }
-
- Ok(blinded_hops)
-}
+ /// Create a blinded path for a payment, to be forwarded along `intermediate_nodes`.
+ ///
+ /// Errors if:
+ /// * a provided node id is invalid
+ /// * [`BlindedPayInfo`] calculation results in an integer overflow
+ /// * any unknown features are required in the provided [`ForwardTlvs`]
+ ///
+ /// [`ForwardTlvs`]: crate::blinded_path::payment::ForwardTlvs
+ // TODO: make all payloads the same size with padding + add dummy hops
+ pub fn new_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
+ intermediate_nodes: &[payment::ForwardNode], payee_node_id: PublicKey,
+ payee_tlvs: payment::ReceiveTlvs, htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
+ entropy_source: &ES, secp_ctx: &Secp256k1<T>
+ ) -> Result<(BlindedPayInfo, Self), ()> {
+ let introduction_node = IntroductionNode::NodeId(
+ intermediate_nodes.first().map_or(payee_node_id, |n| n.node_id)
+ );
+ let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
+ let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
+
+ let blinded_payinfo = payment::compute_payinfo(
+ intermediate_nodes, &payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
+ )?;
+ Ok((blinded_payinfo, BlindedPath {
+ introduction_node,
+ blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
+ blinded_hops: payment::blinded_hops(
+ secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
+ ).map_err(|_| ())?,
+ }))
+ }