lnonion: rm support for legacy (pre-TLV) onions
see https://github.com/lightning/bolts/pull/962
This commit is contained in:
@@ -42,7 +42,6 @@ if TYPE_CHECKING:
|
||||
|
||||
HOPS_DATA_SIZE = 1300 # also sometimes called routingInfoSize in bolt-04
|
||||
TRAMPOLINE_HOPS_DATA_SIZE = 400
|
||||
LEGACY_PER_HOP_FULL_SIZE = 65
|
||||
PER_HOP_HMAC_SIZE = 32
|
||||
|
||||
|
||||
@@ -51,53 +50,9 @@ class InvalidOnionMac(Exception): pass
|
||||
class InvalidOnionPubkey(Exception): pass
|
||||
|
||||
|
||||
class LegacyHopDataPayload:
|
||||
|
||||
def __init__(self, *, short_channel_id: bytes, amt_to_forward: int, outgoing_cltv_value: int):
|
||||
self.short_channel_id = ShortChannelID(short_channel_id)
|
||||
self.amt_to_forward = amt_to_forward
|
||||
self.outgoing_cltv_value = outgoing_cltv_value
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
ret = self.short_channel_id
|
||||
ret += int.to_bytes(self.amt_to_forward, length=8, byteorder="big", signed=False)
|
||||
ret += int.to_bytes(self.outgoing_cltv_value, length=4, byteorder="big", signed=False)
|
||||
ret += bytes(12) # padding
|
||||
if len(ret) != 32:
|
||||
raise Exception('unexpected length {}'.format(len(ret)))
|
||||
return ret
|
||||
|
||||
def to_tlv_dict(self) -> dict:
|
||||
d = {
|
||||
"amt_to_forward": {"amt_to_forward": self.amt_to_forward},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": self.outgoing_cltv_value},
|
||||
"short_channel_id": {"short_channel_id": self.short_channel_id},
|
||||
}
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, b: bytes) -> 'LegacyHopDataPayload':
|
||||
if len(b) != 32:
|
||||
raise Exception('unexpected length {}'.format(len(b)))
|
||||
return LegacyHopDataPayload(
|
||||
short_channel_id=b[:8],
|
||||
amt_to_forward=int.from_bytes(b[8:16], byteorder="big", signed=False),
|
||||
outgoing_cltv_value=int.from_bytes(b[16:20], byteorder="big", signed=False),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_tlv_dict(cls, d: dict) -> 'LegacyHopDataPayload':
|
||||
return LegacyHopDataPayload(
|
||||
short_channel_id=d["short_channel_id"]["short_channel_id"] if "short_channel_id" in d else b"\x00" * 8,
|
||||
amt_to_forward=d["amt_to_forward"]["amt_to_forward"],
|
||||
outgoing_cltv_value=d["outgoing_cltv_value"]["outgoing_cltv_value"],
|
||||
)
|
||||
|
||||
|
||||
class OnionHopsDataSingle: # called HopData in lnd
|
||||
|
||||
def __init__(self, *, is_tlv_payload: bool, payload: dict = None):
|
||||
self.is_tlv_payload = is_tlv_payload
|
||||
def __init__(self, *, payload: dict = None):
|
||||
if payload is None:
|
||||
payload = {}
|
||||
self.payload = payload
|
||||
@@ -107,29 +62,20 @@ class OnionHopsDataSingle: # called HopData in lnd
|
||||
def to_bytes(self) -> bytes:
|
||||
hmac_ = self.hmac if self.hmac is not None else bytes(PER_HOP_HMAC_SIZE)
|
||||
if self._raw_bytes_payload is not None:
|
||||
ret = write_bigsize_int(len(self._raw_bytes_payload))
|
||||
ret += self._raw_bytes_payload
|
||||
ret = self._raw_bytes_payload
|
||||
ret += hmac_
|
||||
return ret
|
||||
if not self.is_tlv_payload:
|
||||
ret = b"\x00" # realm==0
|
||||
legacy_payload = LegacyHopDataPayload.from_tlv_dict(self.payload)
|
||||
ret += legacy_payload.to_bytes()
|
||||
ret += hmac_
|
||||
if len(ret) != LEGACY_PER_HOP_FULL_SIZE:
|
||||
raise Exception('unexpected length {}'.format(len(ret)))
|
||||
return ret
|
||||
else: # tlv
|
||||
payload_fd = io.BytesIO()
|
||||
OnionWireSerializer.write_tlv_stream(fd=payload_fd,
|
||||
tlv_stream_name="payload",
|
||||
**self.payload)
|
||||
payload_bytes = payload_fd.getvalue()
|
||||
with io.BytesIO() as fd:
|
||||
fd.write(write_bigsize_int(len(payload_bytes)))
|
||||
fd.write(payload_bytes)
|
||||
fd.write(hmac_)
|
||||
return fd.getvalue()
|
||||
# adding TLV payload. note: legacy hop data format no longer supported.
|
||||
payload_fd = io.BytesIO()
|
||||
OnionWireSerializer.write_tlv_stream(fd=payload_fd,
|
||||
tlv_stream_name="payload",
|
||||
**self.payload)
|
||||
payload_bytes = payload_fd.getvalue()
|
||||
with io.BytesIO() as fd:
|
||||
fd.write(write_bigsize_int(len(payload_bytes)))
|
||||
fd.write(payload_bytes)
|
||||
fd.write(hmac_)
|
||||
return fd.getvalue()
|
||||
|
||||
@classmethod
|
||||
def from_fd(cls, fd: io.BytesIO) -> 'OnionHopsDataSingle':
|
||||
@@ -139,14 +85,7 @@ class OnionHopsDataSingle: # called HopData in lnd
|
||||
fd.seek(-1, io.SEEK_CUR) # undo read
|
||||
if first_byte == b'\x00':
|
||||
# legacy hop data format
|
||||
b = fd.read(LEGACY_PER_HOP_FULL_SIZE)
|
||||
if len(b) != LEGACY_PER_HOP_FULL_SIZE:
|
||||
raise Exception(f'unexpected length {len(b)}')
|
||||
ret = OnionHopsDataSingle(is_tlv_payload=False)
|
||||
legacy_payload = LegacyHopDataPayload.from_bytes(b[1:33])
|
||||
ret.payload = legacy_payload.to_tlv_dict()
|
||||
ret.hmac = b[33:]
|
||||
return ret
|
||||
raise Exception("legacy hop data format no longer supported")
|
||||
elif first_byte == b'\x01':
|
||||
# reserved for future use
|
||||
raise Exception("unsupported hop payload: length==1")
|
||||
@@ -155,7 +94,7 @@ class OnionHopsDataSingle: # called HopData in lnd
|
||||
hop_payload = fd.read(hop_payload_length)
|
||||
if hop_payload_length != len(hop_payload):
|
||||
raise Exception(f"unexpected EOF")
|
||||
ret = OnionHopsDataSingle(is_tlv_payload=True)
|
||||
ret = OnionHopsDataSingle()
|
||||
ret.payload = OnionWireSerializer.read_tlv_stream(fd=io.BytesIO(hop_payload),
|
||||
tlv_stream_name="payload")
|
||||
ret.hmac = fd.read(PER_HOP_HMAC_SIZE)
|
||||
@@ -163,7 +102,7 @@ class OnionHopsDataSingle: # called HopData in lnd
|
||||
return ret
|
||||
|
||||
def __repr__(self):
|
||||
return f"<OnionHopsDataSingle. is_tlv_payload={self.is_tlv_payload}. payload={self.payload}. hmac={self.hmac}>"
|
||||
return f"<OnionHopsDataSingle. payload={self.payload}. hmac={self.hmac}>"
|
||||
|
||||
|
||||
class OnionPacket:
|
||||
@@ -226,8 +165,14 @@ def get_shared_secrets_along_route(payment_path_pubkeys: Sequence[bytes],
|
||||
return hop_shared_secrets
|
||||
|
||||
|
||||
def new_onion_packet(payment_path_pubkeys: Sequence[bytes], session_key: bytes,
|
||||
hops_data: Sequence[OnionHopsDataSingle], associated_data: bytes, trampoline=False) -> OnionPacket:
|
||||
def new_onion_packet(
|
||||
payment_path_pubkeys: Sequence[bytes],
|
||||
session_key: bytes,
|
||||
hops_data: Sequence[OnionHopsDataSingle],
|
||||
*,
|
||||
associated_data: bytes,
|
||||
trampoline: bool = False,
|
||||
) -> OnionPacket:
|
||||
num_hops = len(payment_path_pubkeys)
|
||||
assert num_hops == len(hops_data)
|
||||
hop_shared_secrets = get_shared_secrets_along_route(payment_path_pubkeys, session_key)
|
||||
@@ -289,8 +234,7 @@ def calc_hops_data_for_payment(
|
||||
"total_msat": total_msat,
|
||||
"amount_msat": amt
|
||||
}
|
||||
hops_data = [OnionHopsDataSingle(
|
||||
is_tlv_payload=route[-1].has_feature_varonion(), payload=hop_payload)]
|
||||
hops_data = [OnionHopsDataSingle(payload=hop_payload)]
|
||||
# payloads, backwards from last hop (but excluding the first edge):
|
||||
for edge_index in range(len(route) - 1, 0, -1):
|
||||
route_edge = route[edge_index]
|
||||
@@ -304,9 +248,7 @@ def calc_hops_data_for_payment(
|
||||
"short_channel_id": {"short_channel_id": route_edge.short_channel_id},
|
||||
}
|
||||
hops_data.append(
|
||||
OnionHopsDataSingle(
|
||||
is_tlv_payload=route[edge_index-1].has_feature_varonion(),
|
||||
payload=hop_payload))
|
||||
OnionHopsDataSingle(payload=hop_payload))
|
||||
if not is_trampoline:
|
||||
amt += route_edge.fee_for_edge(amt)
|
||||
cltv += route_edge.cltv_expiry_delta
|
||||
|
||||
Reference in New Issue
Block a user