Skip to content

Commit 88e1b56

Browse files
Merge pull request #3125 from valentinewallace/2024-06-async-payments-prefactor
Async payments message encoding and prefactor
2 parents 07d991c + 5c7af8c commit 88e1b56

18 files changed

+342
-39
lines changed

ci/check-cfg-flags.py

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ def check_cfg_tag(cfg):
104104
pass
105105
elif cfg == "splicing":
106106
pass
107+
elif cfg == "async_payments":
108+
pass
107109
else:
108110
print("Bad cfg tag: " + cfg)
109111
assert False

ci/ci-tests.sh

+2
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,5 @@ RUSTFLAGS="--cfg=async_signing" cargo test --verbose --color always -p lightning
179179
RUSTFLAGS="--cfg=dual_funding" cargo test --verbose --color always -p lightning
180180
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
181181
RUSTFLAGS="--cfg=splicing" cargo test --verbose --color always -p lightning
182+
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
183+
RUSTFLAGS="--cfg=async_payments" cargo test --verbose --color always -p lightning

fuzz/src/onion_message.rs

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
1212
use lightning::ln::script::ShutdownScript;
1313
use lightning::offers::invoice::UnsignedBolt12Invoice;
1414
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
15+
use lightning::onion_message::async_payments::{
16+
AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc,
17+
};
1518
use lightning::onion_message::messenger::{
1619
CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger,
1720
PendingOnionMessage, Responder, ResponseInstruction,
@@ -39,6 +42,7 @@ pub fn do_test<L: Logger>(data: &[u8], logger: &L) {
3942
let node_id_lookup = EmptyNodeIdLookUp {};
4043
let message_router = TestMessageRouter {};
4144
let offers_msg_handler = TestOffersMessageHandler {};
45+
let async_payments_msg_handler = TestAsyncPaymentsMessageHandler {};
4246
let custom_msg_handler = TestCustomMessageHandler {};
4347
let onion_messenger = OnionMessenger::new(
4448
&keys_manager,
@@ -47,6 +51,7 @@ pub fn do_test<L: Logger>(data: &[u8], logger: &L) {
4751
&node_id_lookup,
4852
&message_router,
4953
&offers_msg_handler,
54+
&async_payments_msg_handler,
5055
&custom_msg_handler,
5156
);
5257

@@ -105,6 +110,17 @@ impl OffersMessageHandler for TestOffersMessageHandler {
105110
}
106111
}
107112

113+
struct TestAsyncPaymentsMessageHandler {}
114+
115+
impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
116+
fn held_htlc_available(
117+
&self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
118+
) -> ResponseInstruction<ReleaseHeldHtlc> {
119+
ResponseInstruction::NoResponse
120+
}
121+
fn release_held_htlc(&self, _message: ReleaseHeldHtlc) {}
122+
}
123+
108124
#[derive(Debug)]
109125
struct TestCustomMessage {}
110126

lightning-background-processor/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ use core::task;
568568
/// # type NetworkGraph = lightning::routing::gossip::NetworkGraph<Arc<Logger>>;
569569
/// # type P2PGossipSync<UL> = lightning::routing::gossip::P2PGossipSync<Arc<NetworkGraph>, Arc<UL>, Arc<Logger>>;
570570
/// # type ChannelManager<B, F, FE> = lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor<B, F, FE>, B, FE, Logger>;
571-
/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler>;
571+
/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>;
572572
/// # type Scorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<NetworkGraph>, Arc<Logger>>>;
573573
/// # type PeerManager<B, F, FE, UL> = lightning::ln::peer_handler::SimpleArcPeerManager<SocketDescriptor, ChainMonitor<B, F, FE>, B, FE, Arc<UL>, Logger>;
574574
/// #
@@ -996,7 +996,7 @@ mod tests {
996996
type PGS = Arc<P2PGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>>;
997997
type RGS = Arc<RapidGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>>>;
998998

999-
type OM = OnionMessenger<Arc<KeysManager>, Arc<KeysManager>, Arc<test_utils::TestLogger>, Arc<ChannelManager>, Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>, Arc<KeysManager>>>, IgnoringMessageHandler, IgnoringMessageHandler>;
999+
type OM = OnionMessenger<Arc<KeysManager>, Arc<KeysManager>, Arc<test_utils::TestLogger>, Arc<ChannelManager>, Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>, Arc<KeysManager>>>, IgnoringMessageHandler, IgnoringMessageHandler, IgnoringMessageHandler>;
10001000

10011001
struct Node {
10021002
node: Arc<ChannelManager>,
@@ -1291,7 +1291,7 @@ mod tests {
12911291
let best_block = BestBlock::from_network(network);
12921292
let params = ChainParameters { network, best_block };
12931293
let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), router.clone(), logger.clone(), keys_manager.clone(), keys_manager.clone(), keys_manager.clone(), UserConfig::default(), params, genesis_block.header.time));
1294-
let messenger = Arc::new(OnionMessenger::new(keys_manager.clone(), keys_manager.clone(), logger.clone(), manager.clone(), msg_router.clone(), IgnoringMessageHandler {}, IgnoringMessageHandler {}));
1294+
let messenger = Arc::new(OnionMessenger::new(keys_manager.clone(), keys_manager.clone(), logger.clone(), manager.clone(), msg_router.clone(), IgnoringMessageHandler {}, IgnoringMessageHandler {}, IgnoringMessageHandler {}));
12951295
let wallet = Arc::new(TestWallet {});
12961296
let sweeper = Arc::new(OutputSweeper::new(best_block, Arc::clone(&tx_broadcaster), Arc::clone(&fee_estimator),
12971297
None::<Arc<dyn Filter + Sync + Send>>, Arc::clone(&keys_manager), wallet, Arc::clone(&kv_store), Arc::clone(&logger)));

lightning/src/ln/channelmanager.rs

+11
Original file line numberDiff line numberDiff line change
@@ -10377,6 +10377,17 @@ where
1037710377
},
1037810378
}
1037910379
},
10380+
#[cfg(async_payments)]
10381+
OffersMessage::StaticInvoice(_invoice) => {
10382+
match responder {
10383+
Some(responder) => {
10384+
responder.respond(OffersMessage::InvoiceError(
10385+
InvoiceError::from_string("Static invoices not yet supported".to_string())
10386+
))
10387+
},
10388+
None => return ResponseInstruction::NoResponse,
10389+
}
10390+
},
1038010391
OffersMessage::InvoiceError(invoice_error) => {
1038110392
log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
1038210393
ResponseInstruction::NoResponse

lightning/src/ln/functional_test_utils.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ type TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg> = OnionMessenger<
423423
&'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>,
424424
&'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
425425
IgnoringMessageHandler,
426+
IgnoringMessageHandler,
426427
>;
427428

428429
/// For use with [`OnionMessenger`] otherwise `test_restored_packages_retry` will fail. This is
@@ -3258,7 +3259,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
32583259
let dedicated_entropy = DedicatedEntropy(RandomBytes::new([i as u8; 32]));
32593260
let onion_messenger = OnionMessenger::new(
32603261
dedicated_entropy, cfgs[i].keys_manager, cfgs[i].logger, &chan_mgrs[i],
3261-
&cfgs[i].message_router, &chan_mgrs[i], IgnoringMessageHandler {},
3262+
&cfgs[i].message_router, &chan_mgrs[i], IgnoringMessageHandler {}, IgnoringMessageHandler {},
32623263
);
32633264
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
32643265
let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));

lightning/src/ln/offers_tests.rs

+12
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,12 @@ fn extract_invoice_request<'a, 'b, 'c>(
192192
ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
193193
OffersMessage::InvoiceRequest(invoice_request) => (invoice_request, reply_path.unwrap()),
194194
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
195+
#[cfg(async_payments)]
196+
OffersMessage::StaticInvoice(invoice) => panic!("Unexpected static invoice: {:?}", invoice),
195197
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
196198
},
199+
#[cfg(async_payments)]
200+
ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
197201
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
198202
},
199203
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
@@ -207,8 +211,12 @@ fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage)
207211
ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
208212
OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request),
209213
OffersMessage::Invoice(invoice) => invoice,
214+
#[cfg(async_payments)]
215+
OffersMessage::StaticInvoice(invoice) => panic!("Unexpected static invoice: {:?}", invoice),
210216
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
211217
},
218+
#[cfg(async_payments)]
219+
ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
212220
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
213221
},
214222
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
@@ -224,8 +232,12 @@ fn extract_invoice_error<'a, 'b, 'c>(
224232
ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
225233
OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request),
226234
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
235+
#[cfg(async_payments)]
236+
OffersMessage::StaticInvoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
227237
OffersMessage::InvoiceError(error) => error,
228238
},
239+
#[cfg(async_payments)]
240+
ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
229241
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
230242
},
231243
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),

lightning/src/ln/peer_handler.rs

+9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer};
2828
use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor, NextNoiseStep, MessageBuf, MSG_BUF_ALLOC_SIZE};
2929
use crate::ln::wire;
3030
use crate::ln::wire::{Encode, Type};
31+
use crate::onion_message::async_payments::{AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc};
3132
use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage, Responder, ResponseInstruction};
3233
use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
3334
use crate::onion_message::packet::OnionMessageContents;
@@ -148,6 +149,14 @@ impl OffersMessageHandler for IgnoringMessageHandler {
148149
ResponseInstruction::NoResponse
149150
}
150151
}
152+
impl AsyncPaymentsMessageHandler for IgnoringMessageHandler {
153+
fn held_htlc_available(
154+
&self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
155+
) -> ResponseInstruction<ReleaseHeldHtlc> {
156+
ResponseInstruction::NoResponse
157+
}
158+
fn release_held_htlc(&self, _message: ReleaseHeldHtlc) {}
159+
}
151160
impl CustomOnionMessageHandler for IgnoringMessageHandler {
152161
type CustomMessage = Infallible;
153162
fn handle_custom_message(&self, _message: Self::CustomMessage, _responder: Option<Responder>) -> ResponseInstruction<Self::CustomMessage> {

lightning/src/offers/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub mod parse;
2424
mod payer;
2525
pub mod refund;
2626
pub(crate) mod signer;
27-
#[allow(unused)]
28-
pub(crate) mod static_invoice;
27+
#[cfg(async_payments)]
28+
pub mod static_invoice;
2929
#[cfg(test)]
3030
pub(crate) mod test_utils;

lightning/src/offers/offer.rs

+1
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ impl Offer {
665665
self.contents.expects_quantity()
666666
}
667667

668+
#[cfg(async_payments)]
668669
pub(super) fn verify<T: secp256k1::Signing>(
669670
&self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
670671
) -> Result<(OfferId, Option<Keypair>), ()> {

lightning/src/offers/static_invoice.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
1515
use crate::ln::inbound_payment::ExpandedKey;
1616
use crate::ln::msgs::DecodeError;
1717
use crate::offers::invoice::{
18-
check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPathIter,
19-
BlindedPayInfo, BlindedPayInfoIter, FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef,
18+
check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPayInfo,
19+
FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef,
2020
};
2121
use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
2222
use crate::offers::merkle::{
@@ -26,9 +26,7 @@ use crate::offers::offer::{
2626
Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef, Quantity,
2727
};
2828
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
29-
use crate::util::ser::{
30-
HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer,
31-
};
29+
use crate::util::ser::{Iterable, SeekReadable, WithoutLength, Writeable, Writer};
3230
use crate::util::string::PrintableString;
3331
use bitcoin::address::Address;
3432
use bitcoin::blockdata::constants::ChainHash;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Message handling for async payments.
11+
12+
use crate::io;
13+
use crate::ln::msgs::DecodeError;
14+
use crate::onion_message::messenger::PendingOnionMessage;
15+
use crate::onion_message::messenger::{Responder, ResponseInstruction};
16+
use crate::onion_message::packet::OnionMessageContents;
17+
use crate::prelude::*;
18+
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
19+
20+
// TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4.
21+
const HELD_HTLC_AVAILABLE_TLV_TYPE: u64 = 72;
22+
const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74;
23+
24+
/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
25+
///
26+
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
27+
pub trait AsyncPaymentsMessageHandler {
28+
/// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
29+
/// the held funds.
30+
fn held_htlc_available(
31+
&self, message: HeldHtlcAvailable, responder: Option<Responder>,
32+
) -> ResponseInstruction<ReleaseHeldHtlc>;
33+
34+
/// Handle a [`ReleaseHeldHtlc`] message. If authentication of the message succeeds, an HTLC
35+
/// should be released to the corresponding payee.
36+
fn release_held_htlc(&self, message: ReleaseHeldHtlc);
37+
38+
/// Release any [`AsyncPaymentsMessage`]s that need to be sent.
39+
///
40+
/// Typically, this is used for messages initiating an async payment flow rather than in response
41+
/// to another message.
42+
#[cfg(not(c_bindings))]
43+
fn release_pending_messages(&self) -> Vec<PendingOnionMessage<AsyncPaymentsMessage>> {
44+
vec![]
45+
}
46+
47+
/// Release any [`AsyncPaymentsMessage`]s that need to be sent.
48+
///
49+
/// Typically, this is used for messages initiating a payment flow rather than in response to
50+
/// another message.
51+
#[cfg(c_bindings)]
52+
fn release_pending_messages(
53+
&self,
54+
) -> Vec<(
55+
AsyncPaymentsMessage,
56+
crate::onion_message::messenger::Destination,
57+
Option<crate::blinded_path::BlindedPath>,
58+
)> {
59+
vec![]
60+
}
61+
}
62+
63+
/// Possible async payment messages sent and received via an [`OnionMessage`].
64+
///
65+
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
66+
#[derive(Clone, Debug)]
67+
pub enum AsyncPaymentsMessage {
68+
/// An HTLC is being held upstream for the often-offline recipient, to be released via
69+
/// [`ReleaseHeldHtlc`].
70+
HeldHtlcAvailable(HeldHtlcAvailable),
71+
72+
/// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
73+
ReleaseHeldHtlc(ReleaseHeldHtlc),
74+
}
75+
76+
/// An HTLC destined for the recipient of this message is being held upstream. The reply path
77+
/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
78+
/// will cause the upstream HTLC to be released.
79+
#[derive(Clone, Debug)]
80+
pub struct HeldHtlcAvailable {
81+
/// The secret that will be used by the recipient of this message to release the held HTLC.
82+
pub payment_release_secret: [u8; 32],
83+
}
84+
85+
/// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
86+
#[derive(Clone, Debug)]
87+
pub struct ReleaseHeldHtlc {
88+
/// Used to release the HTLC held upstream if it matches the corresponding
89+
/// [`HeldHtlcAvailable::payment_release_secret`].
90+
pub payment_release_secret: [u8; 32],
91+
}
92+
93+
impl OnionMessageContents for ReleaseHeldHtlc {
94+
fn tlv_type(&self) -> u64 {
95+
RELEASE_HELD_HTLC_TLV_TYPE
96+
}
97+
fn msg_type(&self) -> &'static str {
98+
"Release Held HTLC"
99+
}
100+
}
101+
102+
impl_writeable_tlv_based!(HeldHtlcAvailable, {
103+
(0, payment_release_secret, required),
104+
});
105+
106+
impl_writeable_tlv_based!(ReleaseHeldHtlc, {
107+
(0, payment_release_secret, required),
108+
});
109+
110+
impl AsyncPaymentsMessage {
111+
/// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
112+
pub fn is_known_type(tlv_type: u64) -> bool {
113+
match tlv_type {
114+
HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true,
115+
_ => false,
116+
}
117+
}
118+
}
119+
120+
impl OnionMessageContents for AsyncPaymentsMessage {
121+
fn tlv_type(&self) -> u64 {
122+
match self {
123+
Self::HeldHtlcAvailable(_) => HELD_HTLC_AVAILABLE_TLV_TYPE,
124+
Self::ReleaseHeldHtlc(msg) => msg.tlv_type(),
125+
}
126+
}
127+
fn msg_type(&self) -> &'static str {
128+
match &self {
129+
Self::HeldHtlcAvailable(_) => "Held HTLC Available",
130+
Self::ReleaseHeldHtlc(msg) => msg.msg_type(),
131+
}
132+
}
133+
}
134+
135+
impl Writeable for AsyncPaymentsMessage {
136+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
137+
match self {
138+
Self::HeldHtlcAvailable(message) => message.write(w),
139+
Self::ReleaseHeldHtlc(message) => message.write(w),
140+
}
141+
}
142+
}
143+
144+
impl ReadableArgs<u64> for AsyncPaymentsMessage {
145+
fn read<R: io::Read>(r: &mut R, tlv_type: u64) -> Result<Self, DecodeError> {
146+
match tlv_type {
147+
HELD_HTLC_AVAILABLE_TLV_TYPE => Ok(Self::HeldHtlcAvailable(Readable::read(r)?)),
148+
RELEASE_HELD_HTLC_TLV_TYPE => Ok(Self::ReleaseHeldHtlc(Readable::read(r)?)),
149+
_ => Err(DecodeError::InvalidValue),
150+
}
151+
}
152+
}

0 commit comments

Comments
 (0)