@@ -246,6 +246,20 @@ pub trait CustomOnionMessageHandler {
246246 fn read_custom_message < R : io:: Read > ( & self , message_type : u64 , buffer : & mut R ) -> Result < Option < Self :: CustomMessage > , msgs:: DecodeError > ;
247247}
248248
249+ /// An processed incoming onion message, containing either a Forward (another onion)
250+ /// or a Receive payload with decrypted contents
251+ pub enum PeeledOnion < CMH : Deref > where
252+ CMH :: Target : CustomOnionMessageHandler ,
253+ {
254+ /// Forwarded onion, with the next node id and a new onion
255+ Forward ( PublicKey , msgs:: OnionMessage ) ,
256+ /// Received onion message, with decrypted contents, path_id, and reply path
257+ Receive ( OnionMessageContents < <<CMH as Deref >:: Target as CustomOnionMessageHandler >:: CustomMessage > , Option < [ u8 ; 32 ] > , Option < BlindedPath > )
258+ }
259+
260+ /// Errors that may occur when [receiving an onion message].
261+ #[ derive( Debug , PartialEq , Eq ) ]
262+ pub struct ReceiveError { }
249263
250264/// Create an onion message with contents `message` to the destination of `path`.
251265/// Returns (introduction_node_id, onion_msg)
@@ -357,6 +371,103 @@ where
357371 }
358372 }
359373
374+ /// Decode one layer of an incoming onion message
375+ /// Returns either a Forward (another onion message), or Receive (decrypted content)
376+ pub fn peel_onion < T : CustomOnionMessageContents > (
377+ node_signer : & NS ,
378+ secp_ctx : & Secp256k1 < secp256k1:: All > ,
379+ logger : & L ,
380+ custom_handler : & CMH ,
381+ msg : & msgs:: OnionMessage ,
382+ ) -> Result < PeeledOnion < CMH > , ReceiveError > {
383+ let control_tlvs_ss = match node_signer. ecdh ( Recipient :: Node , & msg. blinding_point , None ) {
384+ Ok ( ss) => ss,
385+ Err ( e) => {
386+ log_error ! ( logger, "Failed to retrieve node secret: {:?}" , e) ;
387+ return Err ( ReceiveError { } ) ;
388+ }
389+ } ;
390+ let onion_decode_ss = {
391+ let blinding_factor = {
392+ let mut hmac = HmacEngine :: < Sha256 > :: new ( b"blinded_node_id" ) ;
393+ hmac. input ( control_tlvs_ss. as_ref ( ) ) ;
394+ Hmac :: from_engine ( hmac) . into_inner ( )
395+ } ;
396+ match node_signer. ecdh ( Recipient :: Node , & msg. onion_routing_packet . public_key ,
397+ Some ( & Scalar :: from_be_bytes ( blinding_factor) . unwrap ( ) ) )
398+ {
399+ Ok ( ss) => ss. secret_bytes ( ) ,
400+ Err ( ( ) ) => {
401+ log_trace ! ( logger, "Failed to compute onion packet shared secret" ) ;
402+ return Err ( ReceiveError { } ) ;
403+ }
404+ }
405+ } ;
406+ match onion_utils:: decode_next_untagged_hop (
407+ onion_decode_ss, & msg. onion_routing_packet . hop_data [ ..] , msg. onion_routing_packet . hmac ,
408+ ( control_tlvs_ss, custom_handler. deref ( ) , logger. deref ( ) )
409+ ) {
410+ Ok ( ( Payload :: Receive :: < <<CMH as Deref >:: Target as CustomOnionMessageHandler >:: CustomMessage > {
411+ message, control_tlvs : ReceiveControlTlvs :: Unblinded ( ReceiveTlvs { path_id } ) , reply_path,
412+ } , None ) ) => {
413+ log_trace!( logger,
414+ "Received an onion message with path_id {:02x?} and {} reply_path" ,
415+ path_id, if reply_path. is_some ( ) { "a" } else { "no" } ) ;
416+
417+ Ok ( PeeledOnion :: Receive ( message, path_id, reply_path) )
418+ } ,
419+ Ok ( ( Payload :: Forward ( ForwardControlTlvs :: Unblinded ( ForwardTlvs {
420+ next_node_id, next_blinding_override
421+ } ) ) , Some ( ( next_hop_hmac, new_packet_bytes) ) ) ) => {
422+ // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
423+ // blinded hop and this onion message is destined for us. In this situation, we should keep
424+ // unwrapping the onion layers to get to the final payload. Since we don't have the option
425+ // of creating blinded paths with dummy hops currently, we should be ok to not handle this
426+ // for now.
427+ let new_pubkey = match onion_utils:: next_hop_pubkey ( & secp_ctx, msg. onion_routing_packet . public_key , & onion_decode_ss) {
428+ Ok ( pk) => pk,
429+ Err ( e) => {
430+ log_trace ! ( logger, "Failed to compute next hop packet pubkey: {}" , e) ;
431+ return Err ( ReceiveError { } )
432+ }
433+ } ;
434+ let outgoing_packet = Packet {
435+ version : 0 ,
436+ public_key : new_pubkey,
437+ hop_data : new_packet_bytes,
438+ hmac : next_hop_hmac,
439+ } ;
440+ let onion_message = msgs:: OnionMessage {
441+ blinding_point : match next_blinding_override {
442+ Some ( blinding_point) => blinding_point,
443+ None => {
444+ match onion_utils:: next_hop_pubkey (
445+ & secp_ctx, msg. blinding_point , control_tlvs_ss. as_ref ( )
446+ ) {
447+ Ok ( bp) => bp,
448+ Err ( e) => {
449+ log_trace ! ( logger, "Failed to compute next blinding point: {}" , e) ;
450+ return Err ( ReceiveError { } )
451+ }
452+ }
453+ }
454+ } ,
455+ onion_routing_packet : outgoing_packet,
456+ } ;
457+
458+ Ok ( PeeledOnion :: Forward ( next_node_id, onion_message) )
459+ } ,
460+ Err ( e) => {
461+ log_trace ! ( logger, "Errored decoding onion message packet: {:?}" , e) ;
462+ Err ( ReceiveError { } )
463+ } ,
464+ _ => {
465+ log_trace ! ( logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa" ) ;
466+ Err ( ReceiveError { } )
467+ } ,
468+ }
469+ }
470+
360471 fn respond_with_onion_message < T : CustomOnionMessageContents > (
361472 & self , response : OnionMessageContents < T > , path_id : Option < [ u8 ; 32 ] > ,
362473 reply_path : Option < BlindedPath >
@@ -457,40 +568,14 @@ where
457568 /// soon we'll delegate the onion message to a handler that can generate invoices or send
458569 /// payments.
459570 fn handle_onion_message ( & self , _peer_node_id : & PublicKey , msg : & msgs:: OnionMessage ) {
460- let control_tlvs_ss = match self . node_signer . ecdh ( Recipient :: Node , & msg. blinding_point , None ) {
461- Ok ( ss) => ss,
462- Err ( e) => {
463- log_error ! ( self . logger, "Failed to retrieve node secret: {:?}" , e) ;
464- return
465- }
466- } ;
467- let onion_decode_ss = {
468- let blinding_factor = {
469- let mut hmac = HmacEngine :: < Sha256 > :: new ( b"blinded_node_id" ) ;
470- hmac. input ( control_tlvs_ss. as_ref ( ) ) ;
471- Hmac :: from_engine ( hmac) . into_inner ( )
472- } ;
473- match self . node_signer . ecdh ( Recipient :: Node , & msg. onion_routing_packet . public_key ,
474- Some ( & Scalar :: from_be_bytes ( blinding_factor) . unwrap ( ) ) )
475- {
476- Ok ( ss) => ss. secret_bytes ( ) ,
477- Err ( ( ) ) => {
478- log_trace ! ( self . logger, "Failed to compute onion packet shared secret" ) ;
479- return
480- }
481- }
482- } ;
483- match onion_utils:: decode_next_untagged_hop (
484- onion_decode_ss, & msg. onion_routing_packet . hop_data [ ..] , msg. onion_routing_packet . hmac ,
485- ( control_tlvs_ss, & * self . custom_handler , & * self . logger )
571+ match Self :: peel_onion :: < <<CMH as Deref >:: Target as CustomOnionMessageHandler >:: CustomMessage > (
572+ & self . node_signer ,
573+ & self . secp_ctx ,
574+ & self . logger ,
575+ & self . custom_handler ,
576+ msg
486577 ) {
487- Ok ( ( Payload :: Receive :: < <<CMH as Deref >:: Target as CustomOnionMessageHandler >:: CustomMessage > {
488- message, control_tlvs : ReceiveControlTlvs :: Unblinded ( ReceiveTlvs { path_id } ) , reply_path,
489- } , None ) ) => {
490- log_trace!( self . logger,
491- "Received an onion message with path_id {:02x?} and {} reply_path" ,
492- path_id, if reply_path. is_some ( ) { "a" } else { "no" } ) ;
493-
578+ Ok ( PeeledOnion :: Receive ( message, path_id, reply_path) ) => {
494579 let response = match message {
495580 OnionMessageContents :: Offers ( msg) => {
496581 self . offers_handler . handle_message ( msg)
@@ -501,50 +586,11 @@ where
501586 . map ( |msg| OnionMessageContents :: Custom ( msg) )
502587 } ,
503588 } ;
504-
505589 if let Some ( response) = response {
506590 self . respond_with_onion_message ( response, path_id, reply_path) ;
507591 }
508592 } ,
509- Ok ( ( Payload :: Forward ( ForwardControlTlvs :: Unblinded ( ForwardTlvs {
510- next_node_id, next_blinding_override
511- } ) ) , Some ( ( next_hop_hmac, new_packet_bytes) ) ) ) => {
512- // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
513- // blinded hop and this onion message is destined for us. In this situation, we should keep
514- // unwrapping the onion layers to get to the final payload. Since we don't have the option
515- // of creating blinded paths with dummy hops currently, we should be ok to not handle this
516- // for now.
517- let new_pubkey = match onion_utils:: next_hop_pubkey ( & self . secp_ctx , msg. onion_routing_packet . public_key , & onion_decode_ss) {
518- Ok ( pk) => pk,
519- Err ( e) => {
520- log_trace ! ( self . logger, "Failed to compute next hop packet pubkey: {}" , e) ;
521- return
522- }
523- } ;
524- let outgoing_packet = Packet {
525- version : 0 ,
526- public_key : new_pubkey,
527- hop_data : new_packet_bytes,
528- hmac : next_hop_hmac,
529- } ;
530- let onion_message = msgs:: OnionMessage {
531- blinding_point : match next_blinding_override {
532- Some ( blinding_point) => blinding_point,
533- None => {
534- match onion_utils:: next_hop_pubkey (
535- & self . secp_ctx , msg. blinding_point , control_tlvs_ss. as_ref ( )
536- ) {
537- Ok ( bp) => bp,
538- Err ( e) => {
539- log_trace ! ( self . logger, "Failed to compute next blinding point: {}" , e) ;
540- return
541- }
542- }
543- }
544- } ,
545- onion_routing_packet : outgoing_packet,
546- } ;
547-
593+ Ok ( PeeledOnion :: Forward ( next_node_id, onion_message) ) => {
548594 let mut pending_per_peer_msgs = self . pending_messages . lock ( ) . unwrap ( ) ;
549595 if outbound_buffer_full ( & next_node_id, & pending_per_peer_msgs) {
550596 log_trace ! ( self . logger, "Dropping forwarded onion message to peer {:?}: outbound buffer full" , next_node_id) ;
@@ -563,15 +609,12 @@ where
563609 e. get_mut ( ) . push_back ( onion_message) ;
564610 log_trace ! ( self . logger, "Forwarding an onion message to peer {}" , next_node_id) ;
565611 }
566- } ;
612+ }
567613 } ,
568614 Err ( e) => {
569- log_trace ! ( self . logger, "Errored decoding onion message packet: {:?}" , e) ;
570- } ,
571- _ => {
572- log_trace ! ( self . logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa" ) ;
573- } ,
574- } ;
615+ log_error ! ( self . logger, "Failed to process onion message {:?}" , e) ;
616+ }
617+ }
575618 }
576619
577620 fn peer_connected ( & self , their_node_id : & PublicKey , init : & msgs:: Init , _inbound : bool ) -> Result < ( ) , ( ) > {
0 commit comments