Skip to content

Commit ca2de2b

Browse files
authored
Merge pull request #3793 from jkczyz/2025-05-start-batch
Implement `start_batch` message batching
2 parents 0430b1e + 43db395 commit ca2de2b

File tree

10 files changed

+214
-109
lines changed

10 files changed

+214
-109
lines changed

lightning-net-tokio/src/lib.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ impl Hash for SocketDescriptor {
622622
mod tests {
623623
use bitcoin::constants::ChainHash;
624624
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
625-
use bitcoin::{Network, Txid};
625+
use bitcoin::Network;
626626
use lightning::ln::msgs::*;
627627
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler, PeerManager};
628628
use lightning::ln::types::ChannelId;
@@ -632,7 +632,6 @@ mod tests {
632632

633633
use tokio::sync::mpsc;
634634

635-
use std::collections::BTreeMap;
636635
use std::mem;
637636
use std::sync::atomic::{AtomicBool, Ordering};
638637
use std::sync::{Arc, Mutex};
@@ -726,8 +725,7 @@ mod tests {
726725
}
727726
fn handle_commitment_signed(&self, _their_node_id: PublicKey, _msg: &CommitmentSigned) {}
728727
fn handle_commitment_signed_batch(
729-
&self, _their_node_id: PublicKey, _channel_id: ChannelId,
730-
_batch: BTreeMap<Txid, CommitmentSigned>,
728+
&self, _their_node_id: PublicKey, _channel_id: ChannelId, _batch: Vec<CommitmentSigned>,
731729
) {
732730
}
733731
fn handle_revoke_and_ack(&self, _their_node_id: PublicKey, _msg: &RevokeAndACK) {}

lightning/src/ln/channel.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ use crate::util::errors::APIError;
6868
use crate::util::config::{UserConfig, ChannelConfig, LegacyChannelConfig, ChannelHandshakeConfig, ChannelHandshakeLimits, MaxDustHTLCExposure};
6969
use crate::util::scid_utils::scid_from_parts;
7070

71-
use alloc::collections::BTreeMap;
71+
use alloc::collections::{btree_map, BTreeMap};
7272

7373
use crate::io;
7474
use crate::prelude::*;
@@ -5012,7 +5012,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
50125012
channel_id: self.channel_id,
50135013
htlc_signatures: vec![],
50145014
signature,
5015-
batch: None,
5015+
funding_txid: funding.get_funding_txo().map(|funding_txo| funding_txo.txid),
50165016
#[cfg(taproot)]
50175017
partial_signature_with_nonce: None,
50185018
})
@@ -5991,10 +5991,6 @@ impl<SP: Deref> FundedChannel<SP> where
59915991
)));
59925992
}
59935993

5994-
if msg.batch.is_some() {
5995-
return Err(ChannelError::close("Peer sent initial commitment_signed with a batch".to_owned()));
5996-
}
5997-
59985994
let holder_commitment_point = &mut self.holder_commitment_point.clone();
59995995
self.context.assert_no_commitment_advancement(holder_commitment_point.transaction_number(), "initial commitment_signed");
60005996

@@ -6034,18 +6030,35 @@ impl<SP: Deref> FundedChannel<SP> where
60346030
self.commitment_signed_update_monitor(updates, logger)
60356031
}
60366032

6037-
pub fn commitment_signed_batch<L: Deref>(&mut self, batch: &BTreeMap<Txid, msgs::CommitmentSigned>, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
6033+
pub fn commitment_signed_batch<L: Deref>(&mut self, batch: Vec<msgs::CommitmentSigned>, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
60386034
where L::Target: Logger
60396035
{
60406036
self.commitment_signed_check_state()?;
60416037

6038+
let mut messages = BTreeMap::new();
6039+
for msg in batch {
6040+
let funding_txid = match msg.funding_txid {
6041+
Some(funding_txid) => funding_txid,
6042+
None => {
6043+
return Err(ChannelError::close("Peer sent batched commitment_signed without a funding_txid".to_string()));
6044+
},
6045+
};
6046+
6047+
match messages.entry(funding_txid) {
6048+
btree_map::Entry::Vacant(entry) => { entry.insert(msg); },
6049+
btree_map::Entry::Occupied(_) => {
6050+
return Err(ChannelError::close(format!("Peer sent batched commitment_signed with duplicate funding_txid {}", funding_txid)));
6051+
}
6052+
}
6053+
}
6054+
60426055
// Any commitment_signed not associated with a FundingScope is ignored below if a
60436056
// pending splice transaction has confirmed since receiving the batch.
60446057
let updates = core::iter::once(&self.funding)
60456058
.chain(self.pending_funding.iter())
60466059
.map(|funding| {
60476060
let funding_txid = funding.get_funding_txo().unwrap().txid;
6048-
let msg = batch
6061+
let msg = messages
60496062
.get(&funding_txid)
60506063
.ok_or_else(|| ChannelError::close(format!("Peer did not send a commitment_signed for pending splice transaction: {}", funding_txid)))?;
60516064
self.context
@@ -9392,20 +9405,11 @@ impl<SP: Deref> FundedChannel<SP> where
93929405
}
93939406
}
93949407

9395-
let batch = if self.pending_funding.is_empty() { None } else {
9396-
Some(msgs::CommitmentSignedBatch {
9397-
batch_size: self.pending_funding.len() as u16 + 1,
9398-
funding_txid: funding
9399-
.get_funding_txo()
9400-
.expect("splices should have their funding transactions negotiated before exiting quiescence while un-negotiated splices are discarded on reload")
9401-
.txid,
9402-
})
9403-
};
94049408
Ok(msgs::CommitmentSigned {
94059409
channel_id: self.context.channel_id,
94069410
signature,
94079411
htlc_signatures,
9408-
batch,
9412+
funding_txid: funding.get_funding_txo().map(|funding_txo| funding_txo.txid),
94099413
#[cfg(taproot)]
94109414
partial_signature_with_nonce: None,
94119415
})

lightning/src/ln/channelmanager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9263,7 +9263,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
92639263
}
92649264

92659265
#[rustfmt::skip]
9266-
fn internal_commitment_signed_batch(&self, counterparty_node_id: &PublicKey, channel_id: ChannelId, batch: &BTreeMap<Txid, msgs::CommitmentSigned>) -> Result<(), MsgHandleErrInternal> {
9266+
fn internal_commitment_signed_batch(&self, counterparty_node_id: &PublicKey, channel_id: ChannelId, batch: Vec<msgs::CommitmentSigned>) -> Result<(), MsgHandleErrInternal> {
92679267
let per_peer_state = self.per_peer_state.read().unwrap();
92689268
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
92699269
.ok_or_else(|| {
@@ -12330,9 +12330,9 @@ where
1233012330
}
1233112331

1233212332
#[rustfmt::skip]
12333-
fn handle_commitment_signed_batch(&self, counterparty_node_id: PublicKey, channel_id: ChannelId, batch: BTreeMap<Txid, msgs::CommitmentSigned>) {
12333+
fn handle_commitment_signed_batch(&self, counterparty_node_id: PublicKey, channel_id: ChannelId, batch: Vec<msgs::CommitmentSigned>) {
1233412334
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
12335-
let _ = handle_error!(self, self.internal_commitment_signed_batch(&counterparty_node_id, channel_id, &batch), counterparty_node_id);
12335+
let _ = handle_error!(self, self.internal_commitment_signed_batch(&counterparty_node_id, channel_id, batch), counterparty_node_id);
1233612336
}
1233712337

1233812338
#[rustfmt::skip]

lightning/src/ln/dual_funding_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ fn do_test_v2_channel_establishment(session: V2ChannelEstablishmentTestSession)
185185
)
186186
.unwrap(),
187187
htlc_signatures: vec![],
188-
batch: None,
188+
funding_txid: None,
189189
#[cfg(taproot)]
190190
partial_signature_with_nonce: None,
191191
};

lightning/src/ln/htlc_reserve_unit_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ pub fn test_fee_spike_violation_fails_htlc() {
899899
channel_id: chan.2,
900900
signature: res.0,
901901
htlc_signatures: res.1,
902-
batch: None,
902+
funding_txid: None,
903903
#[cfg(taproot)]
904904
partial_signature_with_nonce: None,
905905
};

lightning/src/ln/msgs.rs

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
4747
#[allow(unused_imports)]
4848
use crate::prelude::*;
4949

50-
use alloc::collections::BTreeMap;
51-
5250
use crate::io::{self, Cursor, Read};
5351
use crate::io_extras::read_to_end;
5452
use core::fmt;
@@ -686,6 +684,20 @@ pub struct ClosingSigned {
686684
pub fee_range: Option<ClosingSignedFeeRange>,
687685
}
688686

687+
/// A [`start_batch`] message to be sent to group together multiple channel messages as a single
688+
/// logical message.
689+
///
690+
/// [`start_batch`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#batching-channel-messages
691+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
692+
pub struct StartBatch {
693+
/// The channel ID of all messages in the batch.
694+
pub channel_id: ChannelId,
695+
/// The number of messages to follow.
696+
pub batch_size: u16,
697+
/// The type of all messages expected in the batch.
698+
pub message_type: Option<u16>,
699+
}
700+
689701
/// An [`update_add_htlc`] message to be sent to or received from a peer.
690702
///
691703
/// [`update_add_htlc`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#adding-an-htlc-update_add_htlc
@@ -795,15 +807,6 @@ pub struct UpdateFailMalformedHTLC {
795807
pub failure_code: u16,
796808
}
797809

798-
/// Optional batch parameters for `commitment_signed` message.
799-
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
800-
pub struct CommitmentSignedBatch {
801-
/// Batch size N: all N `commitment_signed` messages must be received before being processed
802-
pub batch_size: u16,
803-
/// The funding transaction, to discriminate among multiple pending funding transactions (e.g. in case of splicing)
804-
pub funding_txid: Txid,
805-
}
806-
807810
/// A [`commitment_signed`] message to be sent to or received from a peer.
808811
///
809812
/// [`commitment_signed`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#committing-updates-so-far-commitment_signed
@@ -815,8 +818,8 @@ pub struct CommitmentSigned {
815818
pub signature: Signature,
816819
/// Signatures on the HTLC transactions
817820
pub htlc_signatures: Vec<Signature>,
818-
/// Optional batch size and other parameters
819-
pub batch: Option<CommitmentSignedBatch>,
821+
/// The funding transaction, to discriminate among multiple pending funding transactions (e.g. in case of splicing)
822+
pub funding_txid: Option<Txid>,
820823
#[cfg(taproot)]
821824
/// The partial Taproot signature on the commitment transaction
822825
pub partial_signature_with_nonce: Option<PartialSignatureWithNonce>,
@@ -1962,8 +1965,7 @@ pub trait ChannelMessageHandler: BaseMessageHandler {
19621965
fn handle_commitment_signed(&self, their_node_id: PublicKey, msg: &CommitmentSigned);
19631966
/// Handle a batch of incoming `commitment_signed` message from the given peer.
19641967
fn handle_commitment_signed_batch(
1965-
&self, their_node_id: PublicKey, channel_id: ChannelId,
1966-
batch: BTreeMap<Txid, CommitmentSigned>,
1968+
&self, their_node_id: PublicKey, channel_id: ChannelId, batch: Vec<CommitmentSigned>,
19671969
);
19681970
/// Handle an incoming `revoke_and_ack` message from the given peer.
19691971
fn handle_revoke_and_ack(&self, their_node_id: PublicKey, msg: &RevokeAndACK);
@@ -1974,19 +1976,10 @@ pub trait ChannelMessageHandler: BaseMessageHandler {
19741976
) {
19751977
assert!(!batch.is_empty());
19761978
if batch.len() == 1 {
1977-
assert!(batch[0].batch.is_none());
19781979
self.handle_commitment_signed(their_node_id, &batch[0]);
19791980
} else {
19801981
let channel_id = batch[0].channel_id;
1981-
let batch: BTreeMap<Txid, CommitmentSigned> = batch
1982-
.iter()
1983-
.cloned()
1984-
.map(|mut cs| {
1985-
let funding_txid = cs.batch.take().unwrap().funding_txid;
1986-
(funding_txid, cs)
1987-
})
1988-
.collect();
1989-
self.handle_commitment_signed_batch(their_node_id, channel_id, batch);
1982+
self.handle_commitment_signed_batch(their_node_id, channel_id, batch.clone());
19901983
}
19911984
}
19921985

@@ -2756,18 +2749,14 @@ impl_writeable!(ClosingSignedFeeRange, {
27562749
max_fee_satoshis
27572750
});
27582751

2759-
impl_writeable_msg!(CommitmentSignedBatch, {
2760-
batch_size,
2761-
funding_txid,
2762-
}, {});
2763-
27642752
#[cfg(not(taproot))]
27652753
impl_writeable_msg!(CommitmentSigned, {
27662754
channel_id,
27672755
signature,
27682756
htlc_signatures
27692757
}, {
2770-
(0, batch, option),
2758+
// TOOD(splicing): Change this to 1 once the spec is finalized
2759+
(1001, funding_txid, option),
27712760
});
27722761

27732762
#[cfg(taproot)]
@@ -2776,8 +2765,9 @@ impl_writeable_msg!(CommitmentSigned, {
27762765
signature,
27772766
htlc_signatures
27782767
}, {
2779-
(0, batch, option),
27802768
(2, partial_signature_with_nonce, option),
2769+
// TOOD(splicing): Change this to 1 and reorder once the spec is finalized
2770+
(1001, funding_txid, option),
27812771
});
27822772

27832773
impl_writeable!(DecodedOnionErrorPacket, {
@@ -3097,6 +3087,13 @@ impl_writeable_msg!(PeerStorage, { data }, {});
30973087

30983088
impl_writeable_msg!(PeerStorageRetrieval, { data }, {});
30993089

3090+
impl_writeable_msg!(StartBatch, {
3091+
channel_id,
3092+
batch_size
3093+
}, {
3094+
(1, message_type, option)
3095+
});
3096+
31003097
// Note that this is written as a part of ChannelManager objects, and thus cannot change its
31013098
// serialization format in a way which assumes we know the total serialized length/message end
31023099
// position.
@@ -5632,13 +5629,10 @@ mod tests {
56325629
channel_id: ChannelId::from_bytes([2; 32]),
56335630
signature: sig_1,
56345631
htlc_signatures: if htlcs { vec![sig_2, sig_3, sig_4] } else { Vec::new() },
5635-
batch: Some(msgs::CommitmentSignedBatch {
5636-
batch_size: 3,
5637-
funding_txid: Txid::from_str(
5638-
"c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e",
5639-
)
5640-
.unwrap(),
5641-
}),
5632+
funding_txid: Some(
5633+
Txid::from_str("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e")
5634+
.unwrap(),
5635+
),
56425636
#[cfg(taproot)]
56435637
partial_signature_with_nonce: None,
56445638
};
@@ -5649,7 +5643,9 @@ mod tests {
56495643
} else {
56505644
target_value += "0000";
56515645
}
5652-
target_value += "002200036e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2"; // batch
5646+
target_value += "fd03e9"; // Type (funding_txid)
5647+
target_value += "20"; // Length (funding_txid)
5648+
target_value += "6e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2"; // Value
56535649
assert_eq!(encoded_value.as_hex().to_string(), target_value);
56545650
}
56555651

0 commit comments

Comments
 (0)