diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index eb5bd89575a..2dc8ceb6051 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -56,7 +56,7 @@ use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelType #[cfg(any(feature = "_test_utils", test))] use crate::types::features::Bolt11InvoiceFeatures; use crate::routing::router::{BlindedTail, FixedRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router}; -use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundHTLCErr, NextPacketDetails}; +use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundHTLCErr, NextPacketDetails, HopConnector}; use crate::ln::msgs; use crate::ln::onion_utils; use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING}; @@ -131,7 +131,7 @@ pub use crate::ln::outbound_payment::{Bolt12PaymentError, ProbeSendFailure, Retr #[cfg(test)] pub(crate) use crate::ln::outbound_payment::PaymentSendFailure; use crate::ln::script::ShutdownScript; - +use crate::routing::gossip::NodeId; // We hold various information about HTLC relay in the HTLC objects in Channel itself: // // Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should @@ -169,6 +169,25 @@ pub enum PendingHTLCRouting { /// The absolute CLTV of the inbound HTLC incoming_cltv_expiry: Option, }, + + /// An HTLC which should be forwarded on to another Trampoline node. + TrampolineForward { + /// The onion shared secret we build with the sender (or the preceding Trampoline node) used + /// to decrypt the onion. + /// + /// This is later used to encrypt failure packets in the event that the HTLC is failed. + incoming_shared_secret: [u8; 32], + /// The onion which should be included in the forwarded HTLC, telling the next hop what to + /// do with the HTLC. + onion_packet: msgs::TrampolineOnionPacket, + /// The node ID of the Trampoline node which we need to route this HTLC to. + node_id: NodeId, // This should be NonZero eventually when we bump MSRV + /// Set if this HTLC is being forwarded within a blinded path. + blinded: Option, + /// The absolute CLTV of the inbound HTLC + incoming_cltv_expiry: Option, + }, + /// The onion indicates that this is a payment for an invoice (supposedly) generated by us. /// /// Note that at this point, we have not checked that the invoice being paid was actually @@ -279,6 +298,7 @@ impl PendingHTLCRouting { fn incoming_cltv_expiry(&self) -> Option { match self { Self::Forward { incoming_cltv_expiry, .. } => *incoming_cltv_expiry, + Self::TrampolineForward { incoming_cltv_expiry, .. } => *incoming_cltv_expiry, Self::Receive { incoming_cltv_expiry, .. } => Some(*incoming_cltv_expiry), Self::ReceiveKeysend { incoming_cltv_expiry, .. } => Some(*incoming_cltv_expiry), } @@ -4242,11 +4262,15 @@ where // we don't allow forwards outbound over them. return Err(("Refusing to forward to a private channel based on our config.", 0x4000 | 10)); } - if chan.context.get_channel_type().supports_scid_privacy() && next_packet.outgoing_scid != chan.context.outbound_scid_alias() { - // `option_scid_alias` (referred to in LDK as `scid_privacy`) means - // "refuse to forward unless the SCID alias was used", so we pretend - // we don't have the channel here. - return Err(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10)); + if let HopConnector::ShortChannelId(outgoing_scid) = next_packet.outgoing_connector { + if chan.context.get_channel_type().supports_scid_privacy() && outgoing_scid != chan.context.outbound_scid_alias() { + // `option_scid_alias` (referred to in LDK as `scid_privacy`) means + // "refuse to forward unless the SCID alias was used", so we pretend + // we don't have the channel here. + return Err(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10)); + } + } else { + return Err(("Cannot forward by Node ID without SCID.", 0x4000 | 10)); } // Note that we could technically not return an error yet here and just hope @@ -4298,7 +4322,13 @@ where fn can_forward_htlc( &self, msg: &msgs::UpdateAddHTLC, next_packet_details: &NextPacketDetails ) -> Result<(), (&'static str, u16)> { - match self.do_funded_channel_callback(next_packet_details.outgoing_scid, |chan: &mut FundedChannel| { + let outgoing_scid = match next_packet_details.outgoing_connector { + HopConnector::ShortChannelId(scid) => scid, + HopConnector::Trampoline { .. } => { + return Err(("Cannot forward by Node ID without SCID.", 0x4000 | 10)); + } + }; + match self.do_funded_channel_callback(outgoing_scid, |chan: &mut FundedChannel| { self.can_forward_htlc_to_outgoing_channel(chan, msg, next_packet_details) }) { Some(Ok(())) => {}, @@ -4307,8 +4337,8 @@ where // If we couldn't find the channel info for the scid, it may be a phantom or // intercept forward. if (self.default_configuration.accept_intercept_htlcs && - fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)) || - fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash) + fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)) || + fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash) {} else { return Err(("Don't have available channel for forwarding as requested.", 0x4000 | 10)); } @@ -5660,7 +5690,12 @@ where }; let is_intro_node_blinded_forward = next_hop.is_intro_node_blinded_forward(); - let outgoing_scid_opt = next_packet_details_opt.as_ref().map(|d| d.outgoing_scid); + let outgoing_scid_opt = next_packet_details_opt.as_ref().and_then(|d| { + match d.outgoing_connector { + HopConnector::ShortChannelId(scid) => { Some(scid) } + HopConnector::Trampoline { .. } => { None } + } + }); // Process the HTLC on the incoming channel. match self.do_funded_channel_callback(incoming_scid, |chan: &mut FundedChannel| { @@ -8909,6 +8944,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ for (forward_info, prev_htlc_id) in pending_forwards.drain(..) { let scid = match forward_info.routing { PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id, + PendingHTLCRouting::TrampolineForward { .. } => 0, PendingHTLCRouting::Receive { .. } => 0, PendingHTLCRouting::ReceiveKeysend { .. } => 0, }; @@ -12476,6 +12512,13 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting, (9, payment_context, option), (11, invoice_request, option), }, + (3, TrampolineForward) => { + (0, incoming_shared_secret, required), + (1, onion_packet, required), + (2, blinded, option), + (3, node_id, required), + (4, incoming_cltv_expiry, option), + } ); impl_writeable_tlv_based!(PendingHTLCInfo, { diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 8204d90076a..7e445bc08e7 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1775,6 +1775,13 @@ mod fuzzy_internal_msgs { amt_to_forward: u64, outgoing_cltv_value: u32, }, + #[allow(unused)] + TrampolineEntrypoint { + amt_to_forward: u64, + outgoing_cltv_value: u32, + multipath_trampoline_data: Option, + trampoline_packet: TrampolineOnionPacket, + }, Receive { payment_data: Option, payment_metadata: Option>, @@ -1805,6 +1812,36 @@ mod fuzzy_internal_msgs { } } + #[allow(unused)] + pub enum InboundTrampolinePayload { + Forward { + /// The value, in msat, of the payment after this hop's fee is deducted. + amt_to_forward: u64, + outgoing_cltv_value: u32, + /// The node id to which the trampoline node must find a route. + outgoing_node_id: PublicKey, + }, + BlindedForward { + short_channel_id: u64, + payment_relay: PaymentRelay, + payment_constraints: PaymentConstraints, + features: BlindedHopFeatures, + intro_node_blinding_point: Option, + next_blinding_override: Option, + }, + BlindedReceive { + sender_intended_htlc_amt_msat: u64, + total_msat: u64, + cltv_expiry_height: u32, + payment_secret: PaymentSecret, + payment_constraints: PaymentConstraints, + payment_context: PaymentContext, + intro_node_blinding_point: Option, + keysend_preimage: Option, + custom_tlvs: Vec<(u64, Vec)>, + } + } + pub(crate) enum OutboundOnionPayload<'a> { Forward { short_channel_id: u64, @@ -2858,6 +2895,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh let mut payment_metadata: Option>> = None; let mut total_msat = None; let mut keysend_preimage: Option = None; + let mut trampoline_onion_packet: Option = None; let mut invoice_request: Option = None; let mut custom_tlvs = Vec::new(); @@ -2872,6 +2910,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh (12, intro_node_blinding_point, option), (16, payment_metadata, option), (18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))), + (20, trampoline_onion_packet, option), (77_777, invoice_request, option), // See https://github.com/lightning/blips/blob/master/blip-0003.md (5482373484, keysend_preimage, option) @@ -2950,6 +2989,16 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?, outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?, }) + } else if let Some(trampoline_onion_packet) = trampoline_onion_packet { + if payment_metadata.is_some() || encrypted_tlvs_opt.is_some() || + total_msat.is_some() + { return Err(DecodeError::InvalidValue) } + Ok(Self::TrampolineEntrypoint { + amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?, + outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?, + multipath_trampoline_data: payment_data, + trampoline_packet: trampoline_onion_packet, + }) } else { if encrypted_tlvs_opt.is_some() || total_msat.is_some() || invoice_request.is_some() { return Err(DecodeError::InvalidValue) @@ -2971,6 +3020,114 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh } } +impl ReadableArgs<(Option, NS)> for InboundTrampolinePayload where NS::Target: NodeSigner { + fn read(r: &mut R, args: (Option, NS)) -> Result { + let (update_add_blinding_point, node_signer) = args; + + let mut amt = None; + let mut cltv_value = None; + let mut payment_data: Option = None; + let mut encrypted_tlvs_opt: Option>> = None; + let mut intro_node_blinding_point = None; + let mut outgoing_node_id: Option = None; + let mut total_msat = None; + let mut keysend_preimage: Option = None; + let mut custom_tlvs = Vec::new(); + + let tlv_len = ::read(r)?; + let mut rd = FixedLengthReader::new(r, tlv_len.0); + decode_tlv_stream_with_custom_tlv_decode!(&mut rd, { + (2, amt, (option, encoding: (u64, HighZeroBytesDroppedBigSize))), + (4, cltv_value, (option, encoding: (u32, HighZeroBytesDroppedBigSize))), + (8, payment_data, option), + (10, encrypted_tlvs_opt, option), + (12, intro_node_blinding_point, option), + (14, outgoing_node_id, option), + (18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))), + // See https://github.com/lightning/blips/blob/master/blip-0003.md + (5482373484, keysend_preimage, option) + }, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result { + if msg_type < 1 << 16 { return Ok(false) } + let mut value = Vec::new(); + msg_reader.read_to_limit(&mut value, u64::MAX)?; + custom_tlvs.push((msg_type, value)); + Ok(true) + }); + + if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } + if intro_node_blinding_point.is_some() && update_add_blinding_point.is_some() { + return Err(DecodeError::InvalidValue) + } + + if let Some(blinding_point) = intro_node_blinding_point.or(update_add_blinding_point) { + if payment_data.is_some() { + return Err(DecodeError::InvalidValue) + } + let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0; + let enc_tlvs_ss = node_signer.ecdh(Recipient::Node, &blinding_point, None) + .map_err(|_| DecodeError::InvalidValue)?; + let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes()); + let mut s = Cursor::new(&enc_tlvs); + let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64); + match ChaChaPolyReadAdapter::read(&mut reader, rho)? { + ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Forward(ForwardTlvs { + short_channel_id, payment_relay, payment_constraints, features, next_blinding_override + })} => { + if amt.is_some() || cltv_value.is_some() || total_msat.is_some() || + keysend_preimage.is_some() + { + return Err(DecodeError::InvalidValue) + } + Ok(Self::BlindedForward { + short_channel_id, + payment_relay, + payment_constraints, + features, + intro_node_blinding_point, + next_blinding_override, + }) + }, + ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(receive_tlvs) } => { + let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; + let expanded_key = node_signer.get_inbound_payment_key(); + if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { + return Err(DecodeError::InvalidValue); + } + + let UnauthenticatedReceiveTlvs { + payment_secret, payment_constraints, payment_context, + } = tlvs; + if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } + Ok(Self::BlindedReceive { + sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?, + total_msat: total_msat.ok_or(DecodeError::InvalidValue)?, + cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?, + payment_secret, + payment_constraints, + payment_context, + intro_node_blinding_point, + keysend_preimage, + custom_tlvs, + }) + }, + } + } else if let Some(outgoing_node_id) = outgoing_node_id { + if payment_data.is_some() || encrypted_tlvs_opt.is_some() || + total_msat.is_some() + { return Err(DecodeError::InvalidValue) } + Ok(Self::Forward { + outgoing_node_id, + amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?, + outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?, + }) + } else { + // unblinded Trampoline receives are not supported + return Err(DecodeError::InvalidValue); + } + } +} + + impl Writeable for Ping { fn write(&self, w: &mut W) -> Result<(), io::Error> { self.ponglen.write(w)?; diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 8b560f72624..7ad22dadcb9 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -16,7 +16,8 @@ use crate::ln::channelmanager::{BlindedFailure, BlindedForward, CLTV_FAR_FAR_AWA use crate::types::features::BlindedHopFeatures; use crate::ln::msgs; use crate::ln::onion_utils; -use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING}; +use crate::ln::onion_utils::{HTLCFailReason, InboundTrampolineHop, INVALID_ONION_BLINDING}; +use crate::routing::gossip::NodeId; use crate::sign::{NodeSigner, Recipient}; use crate::util::logger::Logger; @@ -98,6 +99,9 @@ pub(super) fn create_fwd_pending_htlc_info( (short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point, next_blinding_override) }, + msgs::InboundOnionPayload::TrampolineEntrypoint {..} => { + todo!() + }, msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } => return Err(InboundHTLCErr { msg: "Final Node OnionHopData provided for us as an intermediary node", @@ -165,6 +169,9 @@ pub(super) fn create_recv_pending_htlc_info( sender_intended_htlc_amt_msat, cltv_expiry_height, None, Some(payment_context), intro_node_blinding_point.is_none(), true, invoice_request) } + msgs::InboundOnionPayload::TrampolineEntrypoint { .. } => { + todo!() + }, msgs::InboundOnionPayload::Forward { .. } => { return Err(InboundHTLCErr { err_code: 0x4000|22, @@ -298,7 +305,7 @@ where Ok(match hop { onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => { let NextPacketDetails { - next_packet_pubkey, outgoing_amt_msat: _, outgoing_scid: _, outgoing_cltv_value + next_packet_pubkey, outgoing_amt_msat: _, outgoing_connector: _, outgoing_cltv_value } = match next_packet_details_opt { Some(next_packet_details) => next_packet_details, // Forward should always include the next hop details @@ -336,9 +343,22 @@ where }) } +pub(super) enum HopConnector { + // scid-based routing + ShortChannelId(u64), + // Trampoline-based routing + #[allow(unused)] + Trampoline { + // shared secret to derive keys for error decoding + shared_secret: [u8; 32], + // node ID to get to the next Trampoline hop + node_id: NodeId, + }, +} + pub(super) struct NextPacketDetails { pub(super) next_packet_pubkey: Result, - pub(super) outgoing_scid: u64, + pub(super) outgoing_connector: HopConnector, pub(super) outgoing_amt_msat: u64, pub(super) outgoing_cltv_value: u32, } @@ -412,7 +432,7 @@ where let next_hop = match onion_utils::decode_next_payment_hop( shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, - msg.payment_hash, msg.blinding_point, node_signer + msg.payment_hash, msg.blinding_point, &(*node_signer) ) { Ok(res) => res, Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => { @@ -432,7 +452,7 @@ where let next_packet_pubkey = onion_utils::next_hop_pubkey(secp_ctx, msg.onion_routing_packet.public_key.unwrap(), &shared_secret); NextPacketDetails { - next_packet_pubkey, outgoing_scid: short_channel_id, + next_packet_pubkey, outgoing_connector: HopConnector::ShortChannelId(short_channel_id), outgoing_amt_msat: amt_to_forward, outgoing_cltv_value } }, @@ -453,10 +473,52 @@ where let next_packet_pubkey = onion_utils::next_hop_pubkey(&secp_ctx, msg.onion_routing_packet.public_key.unwrap(), &shared_secret); NextPacketDetails { - next_packet_pubkey, outgoing_scid: short_channel_id, outgoing_amt_msat: amt_to_forward, + next_packet_pubkey, outgoing_connector: HopConnector::ShortChannelId(short_channel_id), outgoing_amt_msat: amt_to_forward, outgoing_cltv_value } }, + onion_utils::Hop::Forward { + next_hop_data: msgs::InboundOnionPayload::TrampolineEntrypoint { .. }, .. + } => { + return_err!("TrampolineEntrypoint data provided in intermediary node", 0x4000 | 22, &[0; 0]); + }, + onion_utils::Hop::Receive(msgs::InboundOnionPayload::TrampolineEntrypoint { ref trampoline_packet, .. }) => { + { + let trampoline_shared_secret = node_signer.ecdh( + Recipient::Node, &trampoline_packet.public_key, blinded_node_id_tweak.as_ref(), + ).unwrap().secret_bytes(); + let next_trampoline_hop = match onion_utils::decode_next_trampoline_hop( + trampoline_shared_secret, &trampoline_packet.hop_data[..], trampoline_packet.hmac, + msg.payment_hash, msg.blinding_point, node_signer, + ) { + Ok(res) => res, + Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => { + return_malformed_err!(err_msg, err_code); + } + Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code }) => { + return_err!(err_msg, err_code, &[0; 0]); + } + }; + match next_trampoline_hop { + InboundTrampolineHop::Forward { next_hop_data: msgs::InboundTrampolinePayload::Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id }, .. } => { + let next_packet_pubkey = onion_utils::next_hop_pubkey(&secp_ctx, + msg.onion_routing_packet.public_key.unwrap(), &trampoline_shared_secret); + NextPacketDetails { + next_packet_pubkey, + outgoing_connector: HopConnector::Trampoline{ + shared_secret: trampoline_shared_secret, + node_id: NodeId::from_pubkey(&outgoing_node_id), + }, + outgoing_amt_msat: amt_to_forward, + outgoing_cltv_value, + } + } + _ => { + todo!(); + } + } + } + } onion_utils::Hop::Receive { .. } => return Ok((next_hop, shared_secret, None)), onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::Receive { .. }, .. } | onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::BlindedReceive { .. }, .. } => diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 6e0a162406c..ca8f80f8ef2 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -1444,6 +1444,23 @@ impl Hop { } } +/// Data decrypted from a payment's Trampoline onion payload. +#[allow(unused)] +pub(crate) enum InboundTrampolineHop { + /// This onion payload was for us, not for forwarding to a next-hop. Contains information for + /// verifying the incoming payment. + Receive(msgs::InboundTrampolinePayload), + /// This onion payload needs to be forwarded to a next-hop. + Forward { + /// Onion payload data used in forwarding the payment. + next_hop_data: msgs::InboundTrampolinePayload, + /// HMAC of the next hop's onion packet. + next_hop_hmac: [u8; 32], + /// Bytes of the onion packet we're forwarding. + new_packet_bytes: [u8; ONION_DATA_LEN], + }, +} + /// Error returned when we fail to decode the onion packet. #[derive(Debug)] pub(crate) enum OnionDecodeErr { @@ -1475,6 +1492,28 @@ where } } +pub(crate) fn decode_next_trampoline_hop( + shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash, + blinding_point: Option, node_signer: NS, +) -> Result +where + NS::Target: NodeSigner, +{ + match decode_next_hop( + shared_secret, + hop_data, + hmac_bytes, + Some(payment_hash), + (blinding_point, node_signer), + ) { + Ok((next_hop_data, None)) => Ok(InboundTrampolineHop::Receive(next_hop_data)), + Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => { + Ok(InboundTrampolineHop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes }) + }, + Err(e) => Err(e), + } +} + /// Build a payment onion, returning the first hop msat and cltv values as well. /// `cur_block_height` should be set to the best known block height + 1. pub fn create_payment_onion( diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 083cfa2ad6c..21997c09c1a 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -403,10 +403,10 @@ impl MaybeReadable for T { /// /// This is not exported to bindings users as manual TLV building is not currently supported in bindings pub struct RequiredWrapper(pub Option); -impl Readable for RequiredWrapper { +impl LengthReadable for RequiredWrapper { #[inline] - fn read(reader: &mut R) -> Result { - Ok(Self(Some(Readable::read(reader)?))) + fn read_from_fixed_length_buffer(reader: &mut R) -> Result { + Ok(Self(Some(LengthReadable::read_from_fixed_length_buffer(reader)?))) } } impl> ReadableArgs for RequiredWrapper {