connectd: only do lazy transmission for *definitely* non-urgent messages.
What changed, and why it matters
This change adjusts when Core Lightning's networking layer sends messages immediately versus batching them for up to a second. Previously, most messages were treated as non-urgent and could be delayed; now, only a small set of message types (HTLC updates and gossip) are batched, and everything else—including messages from plugins—is sent right away. The patch is framed as a performance/reliability improvement, but it removes a broad delay that could have masked timing-related bugs or made certain attacks easier by giving attackers predictable one-second windows to race against batched messages.
Review whether any protocol messages now sent immediately were previously relying on the one-second batching window for correctness, ordering, or DoS mitigation. In particular, verify that plugin-injected messages and less-common wire types behave safely under immediate transmission. Consider fuzzing or stress-testing connectd under high message load to confirm no new race conditions or amplification issues.
Security signals we found
Behavioral change in message scheduling that reduces attacker-controllable delay windows
Default/fallback policy flipped from batching to immediate transmission
Developer comment explicitly references up-to-1-second delay being too long
No explicit security bug fix language in commit or title
No CVE, advisory, or researcher attribution present in commit
Evidence from the diff
In connectd/multiplex.c, the is_urgent() function is inverted: instead of returning false (non-urgent, batched) for most peer wire message types and true only for a few, it now returns true (urgent, immediate) for almost everything and explicitly lists only UPDATE_ADD_HTLC, UPDATE_FULFILL_HTLC, UPDATE_FAIL_HTLC, UPDATE_FAIL_MALFORMED_HTLC, UPDATE_FEE, CHANNEL_ANNOUNCEMENT, NODE_ANNOUNCEMENT, and CHANNEL_UPDATE as non-urgent. The fallback for unknown/plugin-injected messages changed from false to true. The commit message says this is because the previous lazy-transmission delay of up to one second was too long for most messages.
Changed components
connectd/multiplex.cPeer message transmission schedulingLazy transmission / batching logicPlugin-injected peer messagesInspect captured patch +17 / −13
diff --git a/connectd/multiplex.c b/connectd/multiplex.c
index 26c99814..d371c9a7 100644
--- a/connectd/multiplex.c
+++ b/connectd/multiplex.c
@@ -305,6 +305,21 @@ void setup_peer_gossip_store(struct peer *peer,
static bool is_urgent(enum peer_wire type)
{
switch (type) {
+ /* We are happy to batch UPDATE_ADD messages: it's the
+ * commitment signed which matters. */
+ case WIRE_UPDATE_ADD_HTLC:
+ case WIRE_UPDATE_FULFILL_HTLC:
+ case WIRE_UPDATE_FAIL_HTLC:
+ case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_UPDATE_FEE:
+ /* Gossip messages are also non-urgent */
+ case WIRE_CHANNEL_ANNOUNCEMENT:
+ case WIRE_NODE_ANNOUNCEMENT:
+ case WIRE_CHANNEL_UPDATE:
+ return false;
+
+ /* We don't delay for anything else, but we use a switch
+ * statement to make you think about new cases! */
case WIRE_INIT:
case WIRE_ERROR:
case WIRE_WARNING:
@@ -328,17 +343,9 @@ static bool is_urgent(enum peer_wire type)
case WIRE_CLOSING_SIGNED:
case WIRE_CLOSING_COMPLETE:
case WIRE_CLOSING_SIG:
- case WIRE_UPDATE_ADD_HTLC:
- case WIRE_UPDATE_FULFILL_HTLC:
- case WIRE_UPDATE_FAIL_HTLC:
- case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
- case WIRE_UPDATE_FEE:
case WIRE_UPDATE_BLOCKHEIGHT:
case WIRE_CHANNEL_REESTABLISH:
case WIRE_ANNOUNCEMENT_SIGNATURES:
- case WIRE_CHANNEL_ANNOUNCEMENT:
- case WIRE_NODE_ANNOUNCEMENT:
- case WIRE_CHANNEL_UPDATE:
case WIRE_QUERY_SHORT_CHANNEL_IDS:
case WIRE_REPLY_SHORT_CHANNEL_IDS_END:
case WIRE_QUERY_CHANNEL_RANGE:
@@ -351,9 +358,6 @@ static bool is_urgent(enum peer_wire type)
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
- return false;
-
- /* These are time-sensitive, and so send without delay. */
case WIRE_PING:
case WIRE_PONG:
case WIRE_PROTOCOL_BATCH_ELEMENT:
@@ -363,8 +367,8 @@ static bool is_urgent(enum peer_wire type)
return true;
};
- /* plugins can inject other messages; assume not urgent. */
- return false;
+ /* plugins can inject other messages. */
+ return true;
}
/* Process and eat protocol_batch_element messages, encrypt each element message
Why this scored 46/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.