+
+ /// Releases any [`Self::CustomMessage`]s that need to be sent.
+ ///
+ /// Typically, this is used for messages initiating a message flow rather than in response to
+ /// another message. The latter should use the return value of [`Self::handle_custom_message`].
+ #[cfg(not(c_bindings))]
+ fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>>;
+
+ /// Releases any [`Self::CustomMessage`]s that need to be sent.
+ ///
+ /// Typically, this is used for messages initiating a message flow rather than in response to
+ /// another message. The latter should use the return value of [`Self::handle_custom_message`].
+ #[cfg(c_bindings)]
+ fn release_pending_custom_messages(&self) -> Vec<(Self::CustomMessage, Destination, Option<BlindedPath>)>;
+}
+
+/// A processed incoming onion message, containing either a Forward (another onion message)
+/// or a Receive payload with decrypted contents.
+#[derive(Clone, Debug)]
+pub enum PeeledOnion<T: OnionMessageContents> {
+ /// Forwarded onion, with the next node id and a new onion
+ Forward(NextMessageHop, OnionMessage),
+ /// Received onion message, with decrypted contents, path_id, and reply path
+ Receive(ParsedOnionMessageContents<T>, Option<[u8; 32]>, Option<BlindedPath>)
+}
+
+
+/// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of
+/// `path`, first calling [`Destination::resolve`] on `path.destination` with the given
+/// [`ReadOnlyNetworkGraph`].
+///
+/// Returns the node id of the peer to send the message to, the message itself, and any addresses
+/// needed to connect to the first node.
+pub fn create_onion_message_resolving_destination<
+ ES: Deref, NS: Deref, NL: Deref, T: OnionMessageContents
+>(
+ entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
+ network_graph: &ReadOnlyNetworkGraph, secp_ctx: &Secp256k1<secp256k1::All>,
+ mut path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>,
+) -> Result<(PublicKey, OnionMessage, Option<Vec<SocketAddress>>), SendError>
+where
+ ES::Target: EntropySource,
+ NS::Target: NodeSigner,
+ NL::Target: NodeIdLookUp,
+{
+ path.destination.resolve(network_graph);
+ create_onion_message(
+ entropy_source, node_signer, node_id_lookup, secp_ctx, path, contents, reply_path,
+ )
+}
+
+/// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of
+/// `path`.
+///
+/// Returns the node id of the peer to send the message to, the message itself, and any addresses
+/// needed to connect to the first node.
+///
+/// Returns [`SendError::UnresolvedIntroductionNode`] if:
+/// - `destination` contains a blinded path with an [`IntroductionNode::DirectedShortChannelId`],
+/// - unless it can be resolved by [`NodeIdLookUp::next_node_id`].
+/// Use [`create_onion_message_resolving_destination`] instead to resolve the introduction node
+/// first with a [`ReadOnlyNetworkGraph`].
+pub fn create_onion_message<ES: Deref, NS: Deref, NL: Deref, T: OnionMessageContents>(
+ entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
+ secp_ctx: &Secp256k1<secp256k1::All>, path: OnionMessagePath, contents: T,
+ reply_path: Option<BlindedPath>,
+) -> Result<(PublicKey, OnionMessage, Option<Vec<SocketAddress>>), SendError>
+where
+ ES::Target: EntropySource,
+ NS::Target: NodeSigner,
+ NL::Target: NodeIdLookUp,
+{
+ let OnionMessagePath { intermediate_nodes, mut destination, first_node_addresses } = path;
+ if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination {
+ if blinded_hops.is_empty() {
+ return Err(SendError::TooFewBlindedHops);
+ }
+ }
+
+ if contents.tlv_type() < 64 { return Err(SendError::InvalidMessage) }
+
+ // If we are sending straight to a blinded path and we are the introduction node, we need to
+ // advance the blinded path by 1 hop so the second hop is the new introduction node.
+ if intermediate_nodes.len() == 0 {
+ if let Destination::BlindedPath(ref mut blinded_path) = destination {
+ let our_node_id = node_signer.get_node_id(Recipient::Node)
+ .map_err(|()| SendError::GetNodeIdFailed)?;
+ let introduction_node_id = match blinded_path.introduction_node {
+ IntroductionNode::NodeId(pubkey) => pubkey,
+ IntroductionNode::DirectedShortChannelId(direction, scid) => {
+ match node_id_lookup.next_node_id(scid) {
+ Some(next_node_id) => *direction.select_pubkey(&our_node_id, &next_node_id),
+ None => return Err(SendError::UnresolvedIntroductionNode),
+ }
+ },
+ };
+ if introduction_node_id == our_node_id {
+ advance_path_by_one(blinded_path, node_signer, node_id_lookup, &secp_ctx)
+ .map_err(|()| SendError::BlindedPathAdvanceFailed)?;
+ }
+ }
+ }
+
+ let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
+ let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
+ let (first_node_id, blinding_point) = if let Some(first_node_id) = intermediate_nodes.first() {
+ (*first_node_id, PublicKey::from_secret_key(&secp_ctx, &blinding_secret))
+ } else {
+ match &destination {
+ Destination::Node(pk) => (*pk, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)),
+ Destination::BlindedPath(BlindedPath { introduction_node, blinding_point, .. }) => {
+ match introduction_node {
+ IntroductionNode::NodeId(pubkey) => (*pubkey, *blinding_point),
+ IntroductionNode::DirectedShortChannelId(..) => {
+ return Err(SendError::UnresolvedIntroductionNode);
+ },
+ }
+ }
+ }
+ };
+ let (packet_payloads, packet_keys) = packet_payloads_and_keys(
+ &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret
+ )?;
+
+ let prng_seed = entropy_source.get_secure_random_bytes();
+ let onion_routing_packet = construct_onion_message_packet(
+ packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?;
+
+ let message = OnionMessage { blinding_point, onion_routing_packet };
+ Ok((first_node_id, message, first_node_addresses))
+}
+
+/// Decode one layer of an incoming [`OnionMessage`].
+///
+/// Returns either the next layer of the onion for forwarding or the decrypted content for the
+/// receiver.
+pub fn peel_onion_message<NS: Deref, L: Deref, CMH: Deref>(
+ msg: &OnionMessage, secp_ctx: &Secp256k1<secp256k1::All>, node_signer: NS, logger: L,
+ custom_handler: CMH,
+) -> Result<PeeledOnion<<<CMH>::Target as CustomOnionMessageHandler>::CustomMessage>, ()>
+where
+ NS::Target: NodeSigner,
+ L::Target: Logger,
+ CMH::Target: CustomOnionMessageHandler,
+{
+ let control_tlvs_ss = match node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
+ Ok(ss) => ss,
+ Err(e) => {
+ log_error!(logger, "Failed to retrieve node secret: {:?}", e);
+ return Err(());
+ }
+ };
+ let onion_decode_ss = {
+ let blinding_factor = {
+ let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
+ hmac.input(control_tlvs_ss.as_ref());
+ Hmac::from_engine(hmac).to_byte_array()
+ };
+ match node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
+ Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
+ {
+ Ok(ss) => ss.secret_bytes(),
+ Err(()) => {
+ log_trace!(logger, "Failed to compute onion packet shared secret");
+ return Err(());
+ }
+ }
+ };
+ match onion_utils::decode_next_untagged_hop(
+ onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac,
+ (control_tlvs_ss, custom_handler.deref(), logger.deref())
+ ) {
+ Ok((Payload::Receive::<ParsedOnionMessageContents<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage>> {
+ message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
+ }, None)) => {
+ Ok(PeeledOnion::Receive(message, path_id, reply_path))
+ },
+ Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
+ next_hop, next_blinding_override
+ })), Some((next_hop_hmac, new_packet_bytes)))) => {
+ // TODO: we need to check whether `next_hop` is our node, in which case this is a dummy
+ // blinded hop and this onion message is destined for us. In this situation, we should keep
+ // unwrapping the onion layers to get to the final payload. Since we don't have the option
+ // of creating blinded paths with dummy hops currently, we should be ok to not handle this
+ // for now.
+ let new_pubkey = match onion_utils::next_hop_pubkey(&secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
+ Ok(pk) => pk,
+ Err(e) => {
+ log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e);
+ return Err(())
+ }
+ };
+ let outgoing_packet = Packet {
+ version: 0,
+ public_key: new_pubkey,
+ hop_data: new_packet_bytes,
+ hmac: next_hop_hmac,
+ };
+ let onion_message = OnionMessage {
+ blinding_point: match next_blinding_override {
+ Some(blinding_point) => blinding_point,
+ None => {
+ match onion_utils::next_hop_pubkey(
+ &secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
+ ) {
+ Ok(bp) => bp,
+ Err(e) => {
+ log_trace!(logger, "Failed to compute next blinding point: {}", e);
+ return Err(())
+ }
+ }
+ }
+ },
+ onion_routing_packet: outgoing_packet,
+ };
+
+ Ok(PeeledOnion::Forward(next_hop, onion_message))
+ },
+ Err(e) => {
+ log_trace!(logger, "Errored decoding onion message packet: {:?}", e);
+ Err(())
+ },
+ _ => {
+ log_trace!(logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
+ Err(())
+ },
+ }