Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Make all peer messages constant message size - #8893

Merged
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size
Feb 18, 2026
Merged

Make all peer messages constant message size#8893
rustyrussell merged 13 commits into
ElementsProject:masterfrom
rustyrussell:guilt/constant-message-size

Conversation

@rustyrussell

Copy link
Copy Markdown
Collaborator

Not sufficient, but definitely necessary for avoiding trivial observation.

@rustyrussellrustyrussell added this to the v26.04 milestone Feb 10, 2026
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch 12 times, most recently from 5132083 to efbcb8fCompareFebruary 15, 2026 23:33
l3 doesn't just need to know about l2 (which it can get from the
channel_announcement), but needs to see the node_announcement.
Otherwise:
```
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
# No onion_message support in l1
opts=[{'dev-force-features': -39},
{'dev-allow-localhost': None}])
l3 = node_factory.get_node()
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
wait_for(lambda: l3.rpc.listnodes(l2.info['id'])['nodes'] != [])
offer = l2.rpc.call('offer', {'amount': '2msat',
'description': 'simple test'})
> l3.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
tests/test_pay.py:4804: ... > raise RpcError(method, payload, resp['error'])
E pyln.client.lightning.RpcError: RPC call failed: method: fetchinvoice, payload: {'offer': 'lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqypq5zmnd9khqmr9yp6x2um5zcssxwz9sqkjtd8qwnx06lxckvu6g8w8t0ue0zsrfqqygj636s4sw7v6'}, error: {'code': 1003, 'message': 'Failed: could not route or connect directly to 033845802d25b4e074ccfd7cd8b339a41dc75bf9978a034800444b51d42b07799a: {"code":400,"message":"Unable to connect, no address known for peer"}'}
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Commit 888745b (dev_disconnect:
remove @ marker.) in v0.11 in April 2022) removed the '@' marker from
our dev_disconnect code, but one test still uses it.
Refactoring this code made it crash on invalid input. The test
triggered a db issue which has been long fixed, so I'm simply removing
it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This requires access to dumpcap. On Ubuntu, at least, this means you
need to be in the "wireshark" group.
We may also need:
sudo ethtool -K lo gro off gso off tso off
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Give us a single "next message" function to call. This will be useful
when we want to write more than one at a time.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Do all the special treatment of the message type first.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This gives us finer control over write sizes: for now we just cap
the write size.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're doing our own buffering now.
We leave the is_urgent() function for two commits in the future though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… count.
We are about to use them to make our packet size constant, and this
will upset the tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Messages are now constant.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: we now pad all peer messages to make them the same length.
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from efbcb8f to ab251baCompareFebruary 16, 2026 01:01
This replaces our previous nagle-based toggling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ges.
Since we delay the others quite a lot (up to 1 second), it's better to consider
most messages "urgent" and worth immediately transmitting.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is exactly what membuf is for: it handles expansion much more
neatly.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
rustyrussellforce-pushed the guilt/constant-message-size branch from ab251ba to d20c18aCompareFebruary 16, 2026 03:36
@Lagrang3

Copy link
Copy Markdown
Collaborator

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected.
I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

@rustyrussell

Copy link
Copy Markdown
CollaboratorAuthor

The test added on commit "pytest: add fixture for checking packet sizes." won't fail as expected. I think it is because assert_constant_payload() is only called after the test is completed during cleanup phase.

No, it definitely works (I hit it during debugging!) and I reverted and took off xfail:

tests/test_connection.py::test_constant_packet_size PASSED [100%]
tests/test_connection.py::test_constant_packet_size ERROR [100%]
==================================== ERRORS ====================================
________________ ERROR at teardown of test_constant_packet_size ________________
have_pcap_tools = None
tmp_path = PosixPath('/tmp/pytest-of-rusty/pytest-0/test_constant_packet_size0')
@pytest.fixture
def tcp_capture(have_pcap_tools, tmp_path):
# You will need permissions. Most distributions have a group which has
# permissions to use dumpcap:
# $ ls -l /usr/bin/dumpcap
# -rwxr-xr-- 1 root wireshark 229112 Apr 16 2024 /usr/bin/dumpcap
# $ getcap /usr/bin/dumpcap
# /usr/bin/dumpcap cap_net_admin,cap_net_raw=eip
# So you just need to be in the wireshark group.
cap = TcpCapture(tmp_path)
yield cap
cap.stop()
> cap.assert_constant_payload()
tests/fixtures.py:193: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <fixtures.TcpCapture object at 0x731d573af230>
def assert_constant_payload(self):
tshark_cmd = [
"tshark",
"-r", str(self.pcap),
"-Y", "tcp.len > 0",
"-T", "fields",
"-e", "tcp.len",
]
out = subprocess.check_output(tshark_cmd, text=True)
lengths = [int(x) for x in out.splitlines() if x.strip()]
assert lengths, f"No TCP payload packets captured on port {self.port}"
uniq = set(lengths)
> assert len(uniq) == 1, (
^^^^^^^^^^^^^^

@rustyrussell
rustyrussell merged commit 963b353 into ElementsProject:masterFeb 18, 2026
119 of 127 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rustyrussell@Lagrang3