What changed, and why it matters
This commit is a straightforward renaming of internal variable and wire-format field names to match a recent update in the Lightning BOLT04 specification. Terms like 'blinding' are replaced with 'path_key', and 'next_blinding_override' becomes 'next_path_key_override'. No logic, cryptography, or behavior changes are visible in the diff.
No security action required. Treat as a normal spec-compliance/refactoring change. Ensure downstream consumers of the wire format are aware of the renamed fields if they parse raw onion messages.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch updates Electrum’s Lightning onion-message implementation to align with BOLT04 naming changes (lightning/bolts@3fffab3b889c9a6818130b56fb6dcfdf6906f90e). It renames CSV wire-schema fields (onion_wire.csv, peer_wire.csv), Python dictionary keys in onion_message.py, and corresponding test fixtures/test code. The cryptographic operations (ECDH, SHA256-based blinding derivation, packet construction/forwarding) remain identical; only identifiers and accessor keys change.
Changed components
electrum/lnwire/onion_wire.csvelectrum/lnwire/peer_wire.csvelectrum/onion_message.pytests/blinded-onion-message-onion-test.jsontests/test_onion_message.pyInspect captured patch +52 / −52
diff --git a/electrum/lnwire/onion_wire.csv b/electrum/lnwire/onion_wire.csv
index f37f1b5..08e4039 100644
--- a/electrum/lnwire/onion_wire.csv
+++ b/electrum/lnwire/onion_wire.csv
@@ -34,8 +34,8 @@ tlvtype,encrypted_data_tlv,next_node_id,4
tlvdata,encrypted_data_tlv,next_node_id,node_id,point,
tlvtype,encrypted_data_tlv,path_id,6
tlvdata,encrypted_data_tlv,path_id,data,byte,...
-tlvtype,encrypted_data_tlv,next_blinding_override,8
-tlvdata,encrypted_data_tlv,next_blinding_override,blinding,point,
+tlvtype,encrypted_data_tlv,next_path_key_override,8
+tlvdata,encrypted_data_tlv,next_path_key_override,path_key,point,
tlvtype,encrypted_data_tlv,payment_relay,10
tlvdata,encrypted_data_tlv,payment_relay,cltv_expiry_delta,u16,
tlvdata,encrypted_data_tlv,payment_relay,fee_proportional_millionths,u32,
@@ -112,10 +112,10 @@ tlvtype,onionmsg_tlv,invoice_error,68
tlvdata,onionmsg_tlv,invoice_error,invoice_error,byte,...
subtype,blinded_path
subtypedata,blinded_path,first_node_id,sciddir_or_pubkey,
-subtypedata,blinded_path,blinding,point,
+subtypedata,blinded_path,first_path_key,point,
subtypedata,blinded_path,num_hops,byte,
-subtypedata,blinded_path,path,onionmsg_hop,num_hops
-subtype,onionmsg_hop
-subtypedata,onionmsg_hop,blinded_node_id,point,
-subtypedata,onionmsg_hop,enclen,u16,
-subtypedata,onionmsg_hop,encrypted_recipient_data,byte,enclen
+subtypedata,blinded_path,path,blinded_path_hop,num_hops
+subtype,blinded_path_hop
+subtypedata,blinded_path_hop,blinded_node_id,point,
+subtypedata,blinded_path_hop,enclen,u16,
+subtypedata,blinded_path_hop,encrypted_recipient_data,byte,enclen
diff --git a/electrum/lnwire/peer_wire.csv b/electrum/lnwire/peer_wire.csv
index 6fb630b..2dafaed 100644
--- a/electrum/lnwire/peer_wire.csv
+++ b/electrum/lnwire/peer_wire.csv
@@ -232,6 +232,6 @@ msgdata,gossip_timestamp_filter,chain_hash,chain_hash,
msgdata,gossip_timestamp_filter,first_timestamp,u32,
msgdata,gossip_timestamp_filter,timestamp_range,u32,
msgtype,onion_message,513
-msgdata,onion_message,blinding,point,
+msgdata,onion_message,path_key,point,
msgdata,onion_message,len,u16,
msgdata,onion_message,onion_message_packet,byte,len
diff --git a/electrum/onion_message.py b/electrum/onion_message.py
index faccefe..9c4e570 100644
--- a/electrum/onion_message.py
+++ b/electrum/onion_message.py
@@ -112,7 +112,7 @@ def create_blinded_path(
blinded_path = {
'first_node_id': introduction_point,
- 'blinding': blinding,
+ 'first_path_key': blinding,
'num_hops': len(onionmsg_hops),
'path': onionmsg_hops
}
@@ -218,7 +218,7 @@ def send_onion_message_to(
if lnwallet.node_keypair.pubkey == introduction_point:
# blinded path introduction point is me
- our_blinding = blinded_path['blinding']
+ our_blinding = blinded_path['first_path_key']
our_payload = blinded_path['path'][0]
remaining_blinded_path = blinded_path['path'][1:]
assert len(remaining_blinded_path) > 0, 'sending to myself?'
@@ -234,17 +234,17 @@ def send_onion_message_to(
assert peer, 'next_node_id not a peer'
# blinding override?
- next_blinding_override = recipient_data.get('next_blinding_override')
- if next_blinding_override:
- next_blinding = next_blinding_override.get('blinding')
+ next_path_key_override = recipient_data.get('next_path_key_override')
+ if next_path_key_override:
+ next_path_key = next_path_key_override.get('path_key')
else:
# E_i+1=SHA256(E_i||ss_i) * E_i
blinding_factor = sha256(our_blinding + shared_secret)
blinding_factor_int = int.from_bytes(blinding_factor, byteorder="big")
next_public_key_int = ecc.ECPubkey(our_blinding) * blinding_factor_int
- next_blinding = next_public_key_int.get_public_key_bytes()
+ next_path_key = next_public_key_int.get_public_key_bytes()
- blinding = next_blinding
+ path_key = next_path_key
else:
# we need a route to introduction point
@@ -253,7 +253,7 @@ def send_onion_message_to(
# if blinded path introduction point is our direct peer, no need to route-find
if peer:
# start of blinded path is our peer
- blinding = blinded_path['blinding']
+ path_key = blinded_path['first_path_key']
else:
path = create_onion_message_route_to(lnwallet, introduction_point)
@@ -267,7 +267,7 @@ def send_onion_message_to(
path = path[:-1]
if len(path) == 0:
- blinding = blinded_path['blinding']
+ path_key = blinded_path['first_path_key']
else:
payment_path_pubkeys = [edge.end_node for edge in path]
hop_shared_secrets, blinded_node_ids = get_shared_secrets_along_route(
@@ -281,12 +281,12 @@ def send_onion_message_to(
) for x in path[:-1]
]
- # final hop pre-ip, add next_blinding_override
+ # final hop pre-ip, add next_path_key_override
final_hop_pre_ip = OnionHopsDataSingle(
tlv_stream_name='onionmsg_tlv',
blind_fields={
'next_node_id': {'node_id': introduction_point},
- 'next_blinding_override': {'blinding': blinded_path['blinding']},
+ 'next_path_key_override': {'path_key': blinded_path['first_path_key']},
}
)
hops_data.append(final_hop_pre_ip)
@@ -300,7 +300,7 @@ def send_onion_message_to(
'encrypted_recipient_data': encrypted_recipient_data
}
- blinding = ecc.ECPrivkey(session_key).get_public_key_bytes()
+ path_key = ecc.ECPrivkey(session_key).get_public_key_bytes()
# append (remaining) blinded path and payload
blinded_path_blinded_ids = []
@@ -360,11 +360,11 @@ def send_onion_message_to(
packet = new_onion_packet(blinded_node_ids, session_key, hops_data)
packet_b = packet.to_bytes()
- blinding = ecc.ECPrivkey(session_key).get_public_key_bytes()
+ path_key = ecc.ECPrivkey(session_key).get_public_key_bytes()
peer.send_message(
"onion_message",
- blinding=blinding,
+ path_key=path_key,
len=len(packet_b),
onion_message_packet=packet_b
)
@@ -703,27 +703,27 @@ class OnionMessageManager(Logger):
return
# blinding override?
- next_blinding_override = recipient_data.get('next_blinding_override')
- if next_blinding_override:
- next_blinding = next_blinding_override.get('blinding')
+ next_path_key_override = recipient_data.get('next_path_key_override')
+ if next_path_key_override:
+ next_path_key = next_path_key_override.get('path_key')
else:
# E_i+1=SHA256(E_i||ss_i) * E_i
blinding_factor = sha256(blinding + shared_secret)
blinding_factor_int = int.from_bytes(blinding_factor, byteorder="big")
next_public_key_int = ecc.ECPubkey(blinding) * blinding_factor_int
- next_blinding = next_public_key_int.get_public_key_bytes()
+ next_path_key = next_public_key_int.get_public_key_bytes()
if is_dummy_hop:
- self.process_onion_message_packet(next_blinding, onion_packet)
+ self.process_onion_message_packet(next_path_key, onion_packet)
return
- self.submit_forward(onion_packet=onion_packet, blinding=next_blinding, node_id=next_node_id)
+ self.submit_forward(onion_packet=onion_packet, blinding=next_path_key, node_id=next_node_id)
def on_onion_message(self, payload: dict) -> None:
"""handle arriving onion_message."""
- blinding = payload.get('blinding')
- if not blinding:
- self.logger.error('missing blinding')
+ path_key = payload.get('path_key')
+ if not path_key:
+ self.logger.error('missing path_key')
return
packet = payload.get('onion_message_packet')
if payload.get('len', 0) != len(packet):
@@ -733,7 +733,7 @@ class OnionMessageManager(Logger):
self.logger.debug('handling onion message')
onion_packet = OnionPacket.from_bytes(packet)
- self.process_onion_message_packet(blinding, onion_packet)
+ self.process_onion_message_packet(path_key, onion_packet)
def process_onion_message_packet(self, blinding: bytes, onion_packet: OnionPacket) -> None:
our_privkey = blinding_privkey(self.lnwallet.node_keypair.privkey, blinding)
diff --git a/tests/blinded-onion-message-onion-test.json b/tests/blinded-onion-message-onion-test.json
index f66660c..fe5191e 100644
--- a/tests/blinded-onion-message-onion-test.json
+++ b/tests/blinded-onion-message-onion-test.json
@@ -6,12 +6,12 @@
"hops": [
{
"alias": "Alice",
- "comment": "Alice->Bob: note next_blinding_override to match that give by Dave for Bob",
- "blinding_secret": "6363636363636363636363636363636363636363636363636363636363636363",
+ "comment": "Alice->Bob: note next_path_key_override to match that give by Dave for Bob",
+ "path_key_secret": "6363636363636363636363636363636363636363636363636363636363636363",
"tlvs": {
"next_node_id": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
- "next_blinding_override": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
- "blinding_override_secret": "0101010101010101010101010101010101010101010101010101010101010101"
+ "next_path_key_override": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
+ "path_key_override_secret": "0101010101010101010101010101010101010101010101010101010101010101"
},
"encrypted_data_tlv": "04210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c0821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"ss": "c04d2a4c518241cb49f2800eea92554cb543f268b4c73f85693541e86d649205",
@@ -26,7 +26,7 @@
{
"alias": "Bob",
"comment": "Bob->Carol",
- "blinding_secret": "0101010101010101010101010101010101010101010101010101010101010101",
+ "path_key_secret": "0101010101010101010101010101010101010101010101010101010101010101",
"tlvs": {
"next_node_id": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007",
"unknown_tag_561": "123456"
@@ -44,7 +44,7 @@
{
"alias": "Carol",
"comment": "Carol->Dave",
- "blinding_secret": "f7ab6dca6152f7b6b0c9d7c82d716af063d72d8eef8816dfc51a8ae828fa7dce",
+ "path_key_secret": "f7ab6dca6152f7b6b0c9d7c82d716af063d72d8eef8816dfc51a8ae828fa7dce",
"tlvs": {
"padding": "0000000000",
"next_node_id": "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991"
@@ -62,7 +62,7 @@
{
"alias": "Dave",
"comment": "Dave is final node, hence path_id",
- "blinding_secret": "5de52bb427cc148bf23e509fdc18012004202517e80abcfde21612ae408e6cea",
+ "path_key_secret": "5de52bb427cc148bf23e509fdc18012004202517e80abcfde21612ae408e6cea",
"tlvs": {
"padding": "",
"path_id": "deadbeefbadc0ffeedeadbeefbadc0ffeedeadbeefbadc0ffeedeadbeefbadc0",
@@ -82,8 +82,8 @@
},
"route": {
"comment": "The resulting blinded route Alice to Dave.",
- "introduction_node_id": "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619",
- "blinding": "031195a8046dcbb8e17034bca630065e7a0982e4e36f6f7e5a8d4554e4846fcd99",
+ "first_node_id": "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619",
+ "first_path_key": "031195a8046dcbb8e17034bca630065e7a0982e4e36f6f7e5a8d4554e4846fcd99",
"hops": [
{
"blinded_node_id": "02d1c3d73f8cac67e7c5b6ec517282d5ba0a52b06a29ec92ff01e12decf76003c1",
diff --git a/tests/test_onion_message.py b/tests/test_onion_message.py
index 3640eae..9f6a65f 100644
--- a/tests/test_onion_message.py
+++ b/tests/test_onion_message.py
@@ -44,13 +44,13 @@ BOB_TLVS = HOPS[1]['tlvs']
CAROL_TLVS = HOPS[2]['tlvs']
DAVE_TLVS = HOPS[3]['tlvs']
-ALICE_PUBKEY = bfh(test_vectors['route']['introduction_node_id'])
+ALICE_PUBKEY = bfh(test_vectors['route']['first_node_id'])
BOB_PUBKEY = bfh(ALICE_TLVS['next_node_id'])
CAROL_PUBKEY = bfh(BOB_TLVS['next_node_id'])
DAVE_PUBKEY = bfh(CAROL_TLVS['next_node_id'])
-BLINDING_SECRET = bfh(HOPS[0]['blinding_secret'])
-BLINDING_OVERRIDE_SECRET = bfh(ALICE_TLVS['blinding_override_secret'])
+BLINDING_SECRET = bfh(HOPS[0]['path_key_secret'])
+BLINDING_OVERRIDE_SECRET = bfh(ALICE_TLVS['path_key_override_secret'])
SESSION_KEY = bfh(test_vectors['generate']['session_key'])
@@ -74,7 +74,7 @@ class TestOnionMessage(ElectrumTestCase):
tlv_stream_name='onionmsg_tlv',
blind_fields={
'next_node_id': {'node_id': bfh(ALICE_TLVS['next_node_id'])},
- 'next_blinding_override': {'blinding': bfh(ALICE_TLVS['next_blinding_override'])},
+ 'next_path_key_override': {'path_key': bfh(ALICE_TLVS['next_path_key_override'])},
}
),
OnionHopsDataSingle(
@@ -150,7 +150,7 @@ class TestOnionMessage(ElectrumTestCase):
msgtype, data = decode_msg(bfh(msg))
self.assertEqual(msgtype, 'onion_message')
self.assertEqual(data, {
- 'blinding': bfh(test_vectors['route']['blinding']),
+ 'path_key': bfh(test_vectors['route']['first_path_key']),
'len': 1366,
'onion_message_packet': ONION_MESSAGE_PACKET,
})
@@ -158,7 +158,7 @@ class TestOnionMessage(ElectrumTestCase):
def test_decrypt_onion_message(self):
o = OnionPacket.from_bytes(ONION_MESSAGE_PACKET)
our_privkey = bfh(test_vectors['decrypt']['hops'][0]['privkey'])
- blinding = bfh(test_vectors['route']['blinding'])
+ blinding = bfh(test_vectors['route']['first_path_key'])
shared_secret = get_ecdh(our_privkey, blinding)
b_hmac = get_bolt04_onion_key(b'blinded_node_id', shared_secret)
@@ -179,7 +179,7 @@ class TestOnionMessage(ElectrumTestCase):
msgtype, data = decode_msg(bfh(onion_message_bob))
self.assertEqual(msgtype, 'onion_message')
self.assertEqual(data, {
- 'blinding': bfh(ALICE_TLVS['next_blinding_override']),
+ 'path_key': bfh(ALICE_TLVS['next_path_key_override']),
'len': 1366,
'onion_message_packet': p.next_packet.to_bytes(),
})
@@ -196,7 +196,7 @@ class TestOnionMessage(ElectrumTestCase):
rp = create_blinded_path(session_key, [pubkey], final_recipient_data)
self.assertEqual(pubkey, rp['first_node_id'])
- self.assertEqual(bfh('022ed557f5ad336b31a49857e4e9664954ac33385aa20a93e2d64bfe7f08f51277'), rp['blinding'])
+ self.assertEqual(bfh('022ed557f5ad336b31a49857e4e9664954ac33385aa20a93e2d64bfe7f08f51277'), rp['first_path_key'])
self.assertEqual(1, rp['num_hops'])
self.assertEqual([{
'blinded_node_id': bfh('031e5d91e6c417f6e8c16d1086db1887edef7be9334f5e744d04edb8da7507481e'),
@@ -234,7 +234,7 @@ class TestOnionMessage(ElectrumTestCase):
tlv_stream_name='onionmsg_tlv',
blind_fields={
'next_node_id': {'node_id': BOB_PUBKEY},
- 'next_blinding_override': {'blinding': bfh(ALICE_TLVS['next_blinding_override'])},
+ 'next_path_key_override': {'path_key': bfh(ALICE_TLVS['next_path_key_override'])},
}
),
]
@@ -434,7 +434,7 @@ class TestOnionMessageManager(ElectrumTestCase):
onionmsg = bfh(test_vectors['onionmessage']['onion_message_packet'])
try:
t.on_onion_message({
- 'blinding': bfh(test_vectors['route']['blinding']),
+ 'blinding': bfh(test_vectors['route']['first_path_key']),
'len': len(onionmsg),
'onion_message_packet': onionmsg
})
Why this scored 15/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.