mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-28 16:01:30 +00:00
lnonion: implement basis of varonion support
This commit is contained in:
parent
6ba08cc8d4
commit
a66437f399
5 changed files with 326 additions and 107 deletions
|
@ -4,6 +4,8 @@ import io
|
|||
from typing import Callable, Tuple, Any, Dict, List, Sequence, Union, Optional
|
||||
from collections import OrderedDict
|
||||
|
||||
from .lnutil import OnionFailureCodeMetaFlag
|
||||
|
||||
|
||||
class MalformedMsg(Exception): pass
|
||||
class UnknownMsgFieldType(MalformedMsg): pass
|
||||
|
@ -254,8 +256,19 @@ def _resolve_field_count(field_count_str: str, *, vars_dict: dict, allow_any=Fal
|
|||
return field_count
|
||||
|
||||
|
||||
def _parse_msgtype_intvalue_for_onion_wire(value: str) -> int:
|
||||
msg_type_int = 0
|
||||
for component in value.split("|"):
|
||||
try:
|
||||
msg_type_int |= int(component)
|
||||
except ValueError:
|
||||
msg_type_int |= OnionFailureCodeMetaFlag[component]
|
||||
return msg_type_int
|
||||
|
||||
|
||||
class LNSerializer:
|
||||
def __init__(self):
|
||||
|
||||
def __init__(self, *, for_onion_wire: bool = False):
|
||||
# TODO msg_type could be 'int' everywhere...
|
||||
self.msg_scheme_from_type = {} # type: Dict[bytes, List[Sequence[str]]]
|
||||
self.msg_type_from_name = {} # type: Dict[str, bytes]
|
||||
|
@ -264,7 +277,10 @@ class LNSerializer:
|
|||
self.in_tlv_stream_get_record_type_from_name = {} # type: Dict[str, Dict[str, int]]
|
||||
self.in_tlv_stream_get_record_name_from_type = {} # type: Dict[str, Dict[int, str]]
|
||||
|
||||
path = os.path.join(os.path.dirname(__file__), "lnwire", "peer_wire.csv")
|
||||
if for_onion_wire:
|
||||
path = os.path.join(os.path.dirname(__file__), "lnwire", "onion_wire.csv")
|
||||
else:
|
||||
path = os.path.join(os.path.dirname(__file__), "lnwire", "peer_wire.csv")
|
||||
with open(path, newline='') as f:
|
||||
csvreader = csv.reader(f)
|
||||
for row in csvreader:
|
||||
|
@ -272,7 +288,10 @@ class LNSerializer:
|
|||
if row[0] == "msgtype":
|
||||
# msgtype,<msgname>,<value>[,<option>]
|
||||
msg_type_name = row[1]
|
||||
msg_type_int = int(row[2])
|
||||
if for_onion_wire:
|
||||
msg_type_int = _parse_msgtype_intvalue_for_onion_wire(str(row[2]))
|
||||
else:
|
||||
msg_type_int = int(row[2])
|
||||
msg_type_bytes = msg_type_int.to_bytes(2, 'big')
|
||||
assert msg_type_bytes not in self.msg_scheme_from_type, f"type collision? for {msg_type_name}"
|
||||
assert msg_type_name not in self.msg_type_from_name, f"type collision? for {msg_type_name}"
|
||||
|
@ -475,3 +494,6 @@ class LNSerializer:
|
|||
_inst = LNSerializer()
|
||||
encode_msg = _inst.encode_msg
|
||||
decode_msg = _inst.decode_msg
|
||||
|
||||
|
||||
OnionWireSerializer = LNSerializer(for_onion_wire=True)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import io
|
||||
import hashlib
|
||||
from typing import Sequence, List, Tuple, NamedTuple, TYPE_CHECKING
|
||||
from enum import IntEnum, IntFlag
|
||||
|
@ -31,15 +32,16 @@ from . import ecc
|
|||
from .crypto import sha256, hmac_oneshot, chacha20_encrypt
|
||||
from .util import bh2u, profiler, xor_bytes, bfh
|
||||
from .lnutil import (get_ecdh, PaymentFailure, NUM_MAX_HOPS_IN_PAYMENT_PATH,
|
||||
NUM_MAX_EDGES_IN_PAYMENT_PATH, ShortChannelID)
|
||||
NUM_MAX_EDGES_IN_PAYMENT_PATH, ShortChannelID, OnionFailureCodeMetaFlag)
|
||||
from .lnmsg import OnionWireSerializer, read_bigsize_int, write_bigsize_int
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .lnrouter import LNPaymentRoute
|
||||
|
||||
|
||||
HOPS_DATA_SIZE = 1300 # also sometimes called routingInfoSize in bolt-04
|
||||
PER_HOP_FULL_SIZE = 65 # HOPS_DATA_SIZE / 20
|
||||
NUM_STREAM_BYTES = HOPS_DATA_SIZE + PER_HOP_FULL_SIZE
|
||||
LEGACY_PER_HOP_FULL_SIZE = 65
|
||||
NUM_STREAM_BYTES = 2 * HOPS_DATA_SIZE
|
||||
PER_HOP_HMAC_SIZE = 32
|
||||
|
||||
|
||||
|
@ -48,64 +50,124 @@ class InvalidOnionMac(Exception): pass
|
|||
class InvalidOnionPubkey(Exception): pass
|
||||
|
||||
|
||||
class OnionPerHop:
|
||||
class LegacyHopDataPayload:
|
||||
|
||||
def __init__(self, short_channel_id: bytes, amt_to_forward: bytes, outgoing_cltv_value: bytes):
|
||||
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 += self.amt_to_forward
|
||||
ret += self.outgoing_cltv_value
|
||||
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):
|
||||
def from_bytes(cls, b: bytes) -> 'LegacyHopDataPayload':
|
||||
if len(b) != 32:
|
||||
raise Exception('unexpected length {}'.format(len(b)))
|
||||
return OnionPerHop(
|
||||
return LegacyHopDataPayload(
|
||||
short_channel_id=b[:8],
|
||||
amt_to_forward=b[8:16],
|
||||
outgoing_cltv_value=b[16:20]
|
||||
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"],
|
||||
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, per_hop: OnionPerHop = None):
|
||||
self.realm = 0
|
||||
self.per_hop = per_hop
|
||||
def __init__(self, *, is_tlv_payload: bool, payload: dict = None):
|
||||
self.is_tlv_payload = is_tlv_payload
|
||||
if payload is None:
|
||||
payload = {}
|
||||
self.payload = payload
|
||||
self.hmac = None
|
||||
self._raw_bytes_payload = None # used in unit tests
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
ret = bytes([self.realm])
|
||||
ret += self.per_hop.to_bytes()
|
||||
ret += self.hmac if self.hmac is not None else bytes(PER_HOP_HMAC_SIZE)
|
||||
if len(ret) != PER_HOP_FULL_SIZE:
|
||||
raise Exception('unexpected length {}'.format(len(ret)))
|
||||
return ret
|
||||
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 += 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="tlv_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_bytes(cls, b: bytes):
|
||||
if len(b) != PER_HOP_FULL_SIZE:
|
||||
raise Exception('unexpected length {}'.format(len(b)))
|
||||
ret = OnionHopsDataSingle()
|
||||
ret.realm = b[0]
|
||||
if ret.realm != 0:
|
||||
raise Exception('only realm 0 is supported')
|
||||
ret.per_hop = OnionPerHop.from_bytes(b[1:33])
|
||||
ret.hmac = b[33:]
|
||||
return ret
|
||||
def from_fd(cls, fd: io.BytesIO) -> 'OnionHopsDataSingle':
|
||||
first_byte = fd.read(1)
|
||||
if len(first_byte) == 0:
|
||||
raise Exception(f"unexpected EOF")
|
||||
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
|
||||
elif first_byte == b'\x01':
|
||||
# reserved for future use
|
||||
raise Exception("unsupported hop payload: length==1")
|
||||
else:
|
||||
hop_payload_length = read_bigsize_int(fd)
|
||||
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.payload = OnionWireSerializer.read_tlv_stream(fd=io.BytesIO(hop_payload),
|
||||
tlv_stream_name="tlv_payload")
|
||||
ret.hmac = fd.read(PER_HOP_HMAC_SIZE)
|
||||
assert len(ret.hmac) == PER_HOP_HMAC_SIZE
|
||||
return ret
|
||||
|
||||
|
||||
class OnionPacket:
|
||||
|
||||
def __init__(self, public_key: bytes, hops_data: bytes, hmac: bytes):
|
||||
assert len(public_key) == 33
|
||||
assert len(hops_data) == HOPS_DATA_SIZE
|
||||
assert len(hmac) == PER_HOP_HMAC_SIZE
|
||||
self.version = 0
|
||||
self.public_key = public_key
|
||||
self.hops_data = hops_data # also called RoutingInfo in bolt-04
|
||||
|
@ -163,13 +225,14 @@ def get_shared_secrets_along_route(payment_path_pubkeys: Sequence[bytes],
|
|||
def new_onion_packet(payment_path_pubkeys: Sequence[bytes], session_key: bytes,
|
||||
hops_data: Sequence[OnionHopsDataSingle], associated_data: bytes) -> 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)
|
||||
|
||||
filler = generate_filler(b'rho', num_hops, PER_HOP_FULL_SIZE, hop_shared_secrets)
|
||||
filler = _generate_filler(b'rho', hops_data, hop_shared_secrets)
|
||||
next_hmac = bytes(PER_HOP_HMAC_SIZE)
|
||||
|
||||
# Our starting packet needs to be filled out with random bytes, we
|
||||
# generate some determinstically using the session private key.
|
||||
# generate some deterministically using the session private key.
|
||||
pad_key = get_bolt04_onion_key(b'pad', session_key)
|
||||
mix_header = generate_cipher_stream(pad_key, HOPS_DATA_SIZE)
|
||||
|
||||
|
@ -178,9 +241,10 @@ def new_onion_packet(payment_path_pubkeys: Sequence[bytes], session_key: bytes,
|
|||
rho_key = get_bolt04_onion_key(b'rho', hop_shared_secrets[i])
|
||||
mu_key = get_bolt04_onion_key(b'mu', hop_shared_secrets[i])
|
||||
hops_data[i].hmac = next_hmac
|
||||
stream_bytes = generate_cipher_stream(rho_key, NUM_STREAM_BYTES)
|
||||
mix_header = mix_header[:-PER_HOP_FULL_SIZE]
|
||||
mix_header = hops_data[i].to_bytes() + mix_header
|
||||
stream_bytes = generate_cipher_stream(rho_key, HOPS_DATA_SIZE)
|
||||
hop_data_bytes = hops_data[i].to_bytes()
|
||||
mix_header = mix_header[:-len(hop_data_bytes)]
|
||||
mix_header = hop_data_bytes + mix_header
|
||||
mix_header = xor_bytes(mix_header, stream_bytes)
|
||||
if i == num_hops - 1 and len(filler) != 0:
|
||||
mix_header = mix_header[:-len(filler)] + filler
|
||||
|
@ -203,32 +267,53 @@ def calc_hops_data_for_payment(route: 'LNPaymentRoute', amount_msat: int, final_
|
|||
|
||||
amt = amount_msat
|
||||
cltv = final_cltv
|
||||
hops_data = [OnionHopsDataSingle(OnionPerHop(b"\x00" * 8,
|
||||
amt.to_bytes(8, "big"),
|
||||
cltv.to_bytes(4, "big")))]
|
||||
hop_payload = {
|
||||
"amt_to_forward": {"amt_to_forward": amt},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": cltv},
|
||||
"short_channel_id": {"short_channel_id": b"\x00" * 8}, # TODO omit if tlv
|
||||
}
|
||||
hops_data = [OnionHopsDataSingle(is_tlv_payload=False, # TODO
|
||||
payload=hop_payload)]
|
||||
for route_edge in reversed(route[1:]):
|
||||
hops_data += [OnionHopsDataSingle(OnionPerHop(route_edge.short_channel_id,
|
||||
amt.to_bytes(8, "big"),
|
||||
cltv.to_bytes(4, "big")))]
|
||||
hop_payload = {
|
||||
"amt_to_forward": {"amt_to_forward": amt},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": cltv},
|
||||
"short_channel_id": {"short_channel_id": route_edge.short_channel_id},
|
||||
}
|
||||
hops_data += [OnionHopsDataSingle(is_tlv_payload=False, # TODO
|
||||
payload=hop_payload)]
|
||||
amt += route_edge.fee_for_edge(amt)
|
||||
cltv += route_edge.cltv_expiry_delta
|
||||
hops_data.reverse()
|
||||
return hops_data, amt, cltv
|
||||
|
||||
|
||||
def generate_filler(key_type: bytes, num_hops: int, hop_size: int,
|
||||
shared_secrets: Sequence[bytes]) -> bytes:
|
||||
filler_size = (NUM_MAX_HOPS_IN_PAYMENT_PATH + 1) * hop_size
|
||||
def _generate_filler(key_type: bytes, hops_data: Sequence[OnionHopsDataSingle],
|
||||
shared_secrets: Sequence[bytes]) -> bytes:
|
||||
num_hops = len(hops_data)
|
||||
|
||||
# generate filler that matches all but the last hop (no HMAC for last hop)
|
||||
filler_size = 0
|
||||
for hop_data in hops_data[:-1]:
|
||||
filler_size += len(hop_data.to_bytes())
|
||||
filler = bytearray(filler_size)
|
||||
|
||||
for i in range(0, num_hops-1): # -1, as last hop does not obfuscate
|
||||
filler = filler[hop_size:]
|
||||
filler += bytearray(hop_size)
|
||||
stream_key = get_bolt04_onion_key(key_type, shared_secrets[i])
|
||||
stream_bytes = generate_cipher_stream(stream_key, filler_size)
|
||||
filler = xor_bytes(filler, stream_bytes)
|
||||
# Sum up how many frames were used by prior hops.
|
||||
filler_start = HOPS_DATA_SIZE
|
||||
for hop_data in hops_data[:i]:
|
||||
filler_start -= len(hop_data.to_bytes())
|
||||
# The filler is the part dangling off of the end of the
|
||||
# routingInfo, so offset it from there, and use the current
|
||||
# hop's frame count as its size.
|
||||
filler_end = HOPS_DATA_SIZE + len(hops_data[i].to_bytes())
|
||||
|
||||
return filler[(NUM_MAX_HOPS_IN_PAYMENT_PATH-num_hops+2)*hop_size:]
|
||||
stream_key = get_bolt04_onion_key(key_type, shared_secrets[i])
|
||||
stream_bytes = generate_cipher_stream(stream_key, NUM_STREAM_BYTES)
|
||||
filler = xor_bytes(filler, stream_bytes[filler_start:filler_end])
|
||||
filler += bytes(filler_size - len(filler)) # right pad with zeroes
|
||||
|
||||
return filler
|
||||
|
||||
|
||||
def generate_cipher_stream(stream_key: bytes, num_bytes: int) -> bytes:
|
||||
|
@ -260,8 +345,9 @@ def process_onion_packet(onion_packet: OnionPacket, associated_data: bytes,
|
|||
# peel an onion layer off
|
||||
rho_key = get_bolt04_onion_key(b'rho', shared_secret)
|
||||
stream_bytes = generate_cipher_stream(rho_key, NUM_STREAM_BYTES)
|
||||
padded_header = onion_packet.hops_data + bytes(PER_HOP_FULL_SIZE)
|
||||
padded_header = onion_packet.hops_data + bytes(HOPS_DATA_SIZE)
|
||||
next_hops_data = xor_bytes(padded_header, stream_bytes)
|
||||
next_hops_data_fd = io.BytesIO(next_hops_data)
|
||||
|
||||
# calc next ephemeral key
|
||||
blinding_factor = sha256(onion_packet.public_key + shared_secret)
|
||||
|
@ -269,10 +355,10 @@ def process_onion_packet(onion_packet: OnionPacket, associated_data: bytes,
|
|||
next_public_key_int = ecc.ECPubkey(onion_packet.public_key) * blinding_factor_int
|
||||
next_public_key = next_public_key_int.get_public_key_bytes()
|
||||
|
||||
hop_data = OnionHopsDataSingle.from_bytes(next_hops_data[:PER_HOP_FULL_SIZE])
|
||||
hop_data = OnionHopsDataSingle.from_fd(next_hops_data_fd)
|
||||
next_onion_packet = OnionPacket(
|
||||
public_key=next_public_key,
|
||||
hops_data=next_hops_data[PER_HOP_FULL_SIZE:],
|
||||
hops_data=next_hops_data_fd.read(HOPS_DATA_SIZE),
|
||||
hmac=hop_data.hmac
|
||||
)
|
||||
if hop_data.hmac == bytes(PER_HOP_HMAC_SIZE):
|
||||
|
@ -365,12 +451,7 @@ def get_failure_msg_from_onion_error(decrypted_error_packet: bytes) -> OnionRout
|
|||
return OnionRoutingFailureMessage(failure_code, failure_data)
|
||||
|
||||
|
||||
class OnionFailureCodeMetaFlag(IntFlag):
|
||||
BADONION = 0x8000
|
||||
PERM = 0x4000
|
||||
NODE = 0x2000
|
||||
UPDATE = 0x1000
|
||||
|
||||
# TODO maybe we should rm this and just use OnionWireSerializer and onion_wire.csv
|
||||
BADONION = OnionFailureCodeMetaFlag.BADONION
|
||||
PERM = OnionFailureCodeMetaFlag.PERM
|
||||
NODE = OnionFailureCodeMetaFlag.NODE
|
||||
|
|
|
@ -1045,7 +1045,7 @@ class Peer(Logger):
|
|||
local_height = self.network.get_local_height()
|
||||
# create onion packet
|
||||
final_cltv = local_height + min_final_cltv_expiry
|
||||
hops_data, amount_msat, cltv = calc_hops_data_for_payment(route, amount_msat, final_cltv)
|
||||
hops_data, amount_msat, cltv = calc_hops_data_for_payment(route, amount_msat, final_cltv) # TODO varonion
|
||||
assert final_cltv <= cltv, (final_cltv, cltv)
|
||||
secret_key = os.urandom(32)
|
||||
onion = new_onion_packet([x.node_id for x in route], secret_key, hops_data, associated_data=payment_hash)
|
||||
|
@ -1145,9 +1145,8 @@ class Peer(Logger):
|
|||
if not forwarding_enabled:
|
||||
self.logger.info(f"forwarding is disabled. failing htlc.")
|
||||
return OnionRoutingFailureMessage(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
|
||||
dph = processed_onion.hop_data.per_hop
|
||||
next_chan = self.lnworker.get_channel_by_short_id(dph.short_channel_id)
|
||||
next_chan_scid = dph.short_channel_id
|
||||
next_chan_scid = processed_onion.hop_data.payload["short_channel_id"]["short_channel_id"]
|
||||
next_chan = self.lnworker.get_channel_by_short_id(next_chan_scid)
|
||||
local_height = self.network.get_local_height()
|
||||
if next_chan is None:
|
||||
self.logger.info(f"cannot forward htlc. cannot find next_chan {next_chan_scid}")
|
||||
|
@ -1159,7 +1158,7 @@ class Peer(Logger):
|
|||
f"chan state {next_chan.get_state()}, peer state: {next_chan.peer_state}")
|
||||
data = outgoing_chan_upd_len + outgoing_chan_upd
|
||||
return OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=data)
|
||||
next_cltv_expiry = int.from_bytes(dph.outgoing_cltv_value, 'big')
|
||||
next_cltv_expiry = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
if htlc.cltv_expiry - next_cltv_expiry < NBLOCK_OUR_CLTV_EXPIRY_DELTA:
|
||||
data = htlc.cltv_expiry.to_bytes(4, byteorder="big") + outgoing_chan_upd_len + outgoing_chan_upd
|
||||
return OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_CLTV_EXPIRY, data=data)
|
||||
|
@ -1169,7 +1168,7 @@ class Peer(Logger):
|
|||
return OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_SOON, data=data)
|
||||
if max(htlc.cltv_expiry, next_cltv_expiry) > local_height + lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
||||
return OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_FAR, data=b'')
|
||||
next_amount_msat_htlc = int.from_bytes(dph.amt_to_forward, 'big')
|
||||
next_amount_msat_htlc = processed_onion.hop_data.payload["amt_to_forward"]["amt_to_forward"]
|
||||
forwarding_fees = fee_for_edge_msat(
|
||||
forwarded_amount_msat=next_amount_msat_htlc,
|
||||
fee_base_msat=lnutil.OUR_FEE_BASE_MSAT,
|
||||
|
@ -1190,8 +1189,8 @@ class Peer(Logger):
|
|||
"update_add_htlc",
|
||||
channel_id=next_chan.channel_id,
|
||||
id=next_htlc.htlc_id,
|
||||
cltv_expiry=dph.outgoing_cltv_value,
|
||||
amount_msat=dph.amt_to_forward,
|
||||
cltv_expiry=next_cltv_expiry,
|
||||
amount_msat=next_amount_msat_htlc,
|
||||
payment_hash=next_htlc.payment_hash,
|
||||
onion_routing_packet=processed_onion.next_packet.to_bytes()
|
||||
)
|
||||
|
@ -1218,12 +1217,12 @@ class Peer(Logger):
|
|||
if local_height + MIN_FINAL_CLTV_EXPIRY_ACCEPTED > htlc.cltv_expiry:
|
||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_EXPIRY_TOO_SOON, data=b'')
|
||||
return False, reason
|
||||
cltv_from_onion = int.from_bytes(processed_onion.hop_data.per_hop.outgoing_cltv_value, byteorder="big")
|
||||
cltv_from_onion = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
if cltv_from_onion != htlc.cltv_expiry:
|
||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_INCORRECT_CLTV_EXPIRY,
|
||||
data=htlc.cltv_expiry.to_bytes(4, byteorder="big"))
|
||||
return False, reason
|
||||
amount_from_onion = int.from_bytes(processed_onion.hop_data.per_hop.amt_to_forward, byteorder="big")
|
||||
amount_from_onion = processed_onion.hop_data.payload["amt_to_forward"]["amt_to_forward"]
|
||||
if amount_from_onion > htlc.amount_msat:
|
||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_INCORRECT_HTLC_AMOUNT,
|
||||
data=htlc.amount_msat.to_bytes(8, byteorder="big"))
|
||||
|
|
|
@ -1075,3 +1075,11 @@ class UpdateAddHtlc:
|
|||
|
||||
def to_tuple(self):
|
||||
return (self.amount_msat, self.payment_hash, self.cltv_expiry, self.htlc_id, self.timestamp)
|
||||
|
||||
|
||||
class OnionFailureCodeMetaFlag(IntFlag):
|
||||
BADONION = 0x8000
|
||||
PERM = 0x4000
|
||||
NODE = 0x2000
|
||||
UPDATE = 0x1000
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ import shutil
|
|||
import asyncio
|
||||
|
||||
from electrum.util import bh2u, bfh, create_and_start_event_loop
|
||||
from electrum.lnonion import (OnionHopsDataSingle, new_onion_packet, OnionPerHop,
|
||||
from electrum.lnonion import (OnionHopsDataSingle, new_onion_packet,
|
||||
process_onion_packet, _decode_onion_error, decode_onion_error,
|
||||
OnionFailureCode)
|
||||
OnionFailureCode, OnionPacket)
|
||||
from electrum import bitcoin, lnrouter
|
||||
from electrum.constants import BitcoinTestnet
|
||||
from electrum.simple_config import SimpleConfig
|
||||
|
@ -111,7 +111,7 @@ class Test_LNRouter(TestCaseForTestnet):
|
|||
cdb.sql_thread.join(timeout=1)
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
def test_new_onion_packet(self):
|
||||
def test_new_onion_packet_legacy(self):
|
||||
# test vector from bolt-04
|
||||
payment_path_pubkeys = [
|
||||
bfh('02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619'),
|
||||
|
@ -123,28 +123,127 @@ class Test_LNRouter(TestCaseForTestnet):
|
|||
session_key = bfh('4141414141414141414141414141414141414141414141414141414141414141')
|
||||
associated_data = bfh('4242424242424242424242424242424242424242424242424242424242424242')
|
||||
hops_data = [
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0000000000000000'), bfh('0000000000000000'), bfh('00000000')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0101010101010101'), bfh('0000000000000001'), bfh('00000001')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0202020202020202'), bfh('0000000000000002'), bfh('00000002')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0303030303030303'), bfh('0000000000000003'), bfh('00000003')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0404040404040404'), bfh('0000000000000004'), bfh('00000004')
|
||||
)),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 0},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 0},
|
||||
"short_channel_id": {"short_channel_id": bfh('0000000000000000')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 1},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 1},
|
||||
"short_channel_id": {"short_channel_id": bfh('0101010101010101')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 2},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 2},
|
||||
"short_channel_id": {"short_channel_id": bfh('0202020202020202')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 3},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 3},
|
||||
"short_channel_id": {"short_channel_id": bfh('0303030303030303')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 4},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 4},
|
||||
"short_channel_id": {"short_channel_id": bfh('0404040404040404')},
|
||||
}),
|
||||
]
|
||||
packet = new_onion_packet(payment_path_pubkeys, session_key, hops_data, associated_data)
|
||||
self.assertEqual(bfh('0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619e5f14350c2a76fc232b5e46d421e9615471ab9e0bc887beff8c95fdb878f7b3a71e87f9aab8f6378c6ff744c1f34b393ad28d065b535c1a8668d85d3b34a1b3befd10f7d61ab590531cf08000178a333a347f8b4072e216400406bdf3bf038659793a1f9e7abc789266cc861cabd95818c0fc8efbdfdc14e3f7c2bc7eb8d6a79ef75ce721caad69320c3a469a202f3e468c67eaf7a7cda226d0fd32f7b48084dca885d014698cf05d742557763d9cb743faeae65dcc79dddaecf27fe5942be5380d15e9a1ec866abe044a9ad635778ba61fc0776dc832b39451bd5d35072d2269cf9b040a2a2fba158a0d8085926dc2e44f0c88bf487da56e13ef2d5e676a8589881b4869ed4c7f0218ff8c6c7dd7221d189c65b3b9aaa71a01484b122846c7c7b57e02e679ea8469b70e14fe4f70fee4d87b910cf144be6fe48eef24da475c0b0bcc6565a9f99728426ce2380a9580e2a9442481ceae7679906c30b1a0e21a10f26150e0645ab6edfdab1ce8f8bea7b1dee511c5fd38ac0e702c1c15bb86b52bca1b71e15b96982d262a442024c33ceb7dd8f949063c2e5e613e873250e2f8708bd4e1924abd45f65c2fa5617bfb10ee9e4a42d6b5811acc8029c16274f937dac9e8817c7e579fdb767ffe277f26d413ced06b620ede8362081da21cf67c2ca9d6f15fe5bc05f82f5bb93f8916bad3d63338ca824f3bbc11b57ce94a5fa1bc239533679903d6fec92a8c792fd86e2960188c14f21e399cfd72a50c620e10aefc6249360b463df9a89bf6836f4f26359207b765578e5ed76ae9f31b1cc48324be576e3d8e44d217445dba466f9b6293fdf05448584eb64f61e02903f834518622b7d4732471c6e0e22e22d1f45e31f0509eab39cdea5980a492a1da2aaac55a98a01216cd4bfe7abaa682af0fbff2dfed030ba28f1285df750e4d3477190dd193f8643b61d8ac1c427d590badb1f61a05d480908fbdc7c6f0502dd0c4abb51d725e92f95da2a8facb79881a844e2026911adcc659d1fb20a2fce63787c8bb0d9f6789c4b231c76da81c3f0718eb7156565a081d2be6b4170c0e0bcebddd459f53db2590c974bca0d705c055dee8c629bf854a5d58edc85228499ec6dde80cce4c8910b81b1e9e8b0f43bd39c8d69c3a80672729b7dc952dd9448688b6bd06afc2d2819cda80b66c57b52ccf7ac1a86601410d18d0c732f69de792e0894a9541684ef174de766fd4ce55efea8f53812867be6a391ac865802dbc26d93959df327ec2667c7256aa5a1d3c45a69a6158f285d6c97c3b8eedb09527848500517995a9eae4cd911df531544c77f5a9a2f22313e3eb72ca7a07dba243476bc926992e0d1e58b4a2fc8c7b01e0cad726237933ea319bad7537d39f3ed635d1e6c1d29e97b3d2160a09e30ee2b65ac5bce00996a73c008bcf351cecb97b6833b6d121dcf4644260b2946ea204732ac9954b228f0beaa15071930fd9583dfc466d12b5f0eeeba6dcf23d5ce8ae62ee5796359d97a4a15955c778d868d0ef9991d9f2833b5bb66119c5f8b396fd108baed7906cbb3cc376d13551caed97fece6f42a4c908ee279f1127fda1dd3ee77d8de0a6f3c135fa3f1cffe38591b6738dc97b55f0acc52be9753ce53e64d7e497bb00ca6123758df3b68fad99e35c04389f7514a8e36039f541598a417275e77869989782325a15b5342ac5011ff07af698584b476b35d941a4981eac590a07a092bb50342da5d3341f901aa07964a8d02b623c7b106dd0ae50bfa007a22d46c8772fa55558176602946cb1d11ea5460db7586fb89c6d3bcd3ab6dd20df4a4db63d2e7d52380800ad812b8640887e027e946df96488b47fbc4a4fadaa8beda4abe446fafea5403fae2ef'),
|
||||
packet.to_bytes())
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
def test_process_onion_packet(self):
|
||||
def test_new_onion_packet_mixed_payloads(self):
|
||||
# test vector from bolt-04
|
||||
payment_path_pubkeys = [
|
||||
bfh('02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619'),
|
||||
bfh('0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c'),
|
||||
bfh('027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007'),
|
||||
bfh('032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'),
|
||||
bfh('02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145'),
|
||||
]
|
||||
session_key = bfh('4141414141414141414141414141414141414141414141414141414141414141')
|
||||
associated_data = bfh('4242424242424242424242424242424242424242424242424242424242424242')
|
||||
hops_data = [
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 0},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 0},
|
||||
"short_channel_id": {"short_channel_id": bfh('0000000000000000')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=True),
|
||||
OnionHopsDataSingle(is_tlv_payload=True),
|
||||
OnionHopsDataSingle(is_tlv_payload=True),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 4},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 4},
|
||||
"short_channel_id": {"short_channel_id": bfh('0404040404040404')},
|
||||
}),
|
||||
]
|
||||
hops_data[1]._raw_bytes_payload = bfh("0101010101010101000000000000000100000001")
|
||||
hops_data[2]._raw_bytes_payload = bfh("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
|
||||
hops_data[3]._raw_bytes_payload = bfh("0303030303030303000000000000000300000003")
|
||||
packet = new_onion_packet(payment_path_pubkeys, session_key, hops_data, associated_data)
|
||||
self.assertEqual(bfh('0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619e5f14350c2a76fc232b5e46d421e9615471ab9e0bc887beff8c95fdb878f7b3a710f8eaf9ccc768f66bb5dec1f7827f33c43fe2ddd05614c8283aa78e9e7573f87c50f7d61ab590531cf08000178a333a347f8b4072e1cea42da7552402b10765adae3f581408f35ff0a71a34b78b1d8ecae77df96c6404bae9a8e8d7178977d7094a1ae549f89338c0777551f874159eb42d3a59fb9285ad4e24883f27de23942ec966611e99bee1cee503455be9e8e642cef6cef7b9864130f692283f8a973d47a8f1c1726b6e59969385975c766e35737c8d76388b64f748ee7943ffb0e2ee45c57a1abc40762ae598723d21bd184e2b338f68ebff47219357bd19cd7e01e2337b806ef4d717888e129e59cd3dc31e6201ccb2fd6d7499836f37a993262468bcb3a4dcd03a22818aca49c6b7b9b8e9e870045631d8e039b066ff86e0d1b7291f71cefa7264c70404a8e538b566c17ccc5feab231401e6c08a01bd5edfc1aa8e3e533b96e82d1f91118d508924b923531929aea889fcdf057f5995d9731c4bf796fb0e41c885d488dcbc68eb742e27f44310b276edc6f652658149e7e9ced4edde5d38c9b8f92e16f6b4ab13d710ee5c193921909bdd75db331cd9d7581a39fca50814ed8d9d402b86e7f8f6ac2f3bca8e6fe47eb45fbdd3be21a8a8d200797eae3c9a0497132f92410d804977408494dff49dd3d8bce248e0b74fd9e6f0f7102c25ddfa02bd9ad9f746abbfa3379834bc2380d58e9d23237821475a1874484783a15d68f47d3dc339f38d9bf925655d5c946778680fd6d1f062f84128895aff09d35d6c92cca63d3f95a9ee8f2a84f383b4d6a087533e65de12fc8dcaf85777736a2088ff4b22462265028695b37e70963c10df8ef2458756c73007dc3e544340927f9e9f5ea4816a9fd9832c311d122e9512739a6b4714bba590e31caa143ce83cb84b36c738c60c3190ff70cd9ac286a9fd2ab619399b68f1f7447be376ce884b5913c8496d01cbf7a44a60b6e6747513f69dc538f340bc1388e0fde5d0c1db50a4dcb9cc0576e0e2474e4853af9623212578d502757ffb2e0e749695ed70f61c116560d0d4154b64dcf3cbf3c91d89fb6dd004dc19588e3479fcc63c394a4f9e8a3b8b961fce8a532304f1337f1a697a1bb14b94d2953f39b73b6a3125d24f27fcd4f60437881185370bde68a5454d816e7a70d4cea582effab9a4f1b730437e35f7a5c4b769c7b72f0346887c1e63576b2f1e2b3706142586883f8cf3a23595cc8e35a52ad290afd8d2f8bcd5b4c1b891583a4159af7110ecde092079209c6ec46d2bda60b04c519bb8bc6dffb5c87f310814ef2f3003671b3c90ddf5d0173a70504c2280d31f17c061f4bb12a978122c8a2a618bb7d1edcf14f84bf0fa181798b826a254fca8b6d7c81e0beb01bd77f6461be3c8647301d02b04753b0771105986aa0cbc13f7718d64e1b3437e8eef1d319359914a7932548c91570ef3ea741083ca5be5ff43c6d9444d29df06f76ec3dc936e3d180f4b6d0fbc495487c7d44d7c8fe4a70d5ff1461d0d9593f3f898c919c363fa18341ce9dae54f898ccf3fe792136682272941563387263c51b2a2f32363b804672cc158c9230472b554090a661aa81525d11876eefdcc45442249e61e07284592f1606491de5c0324d3af4be035d7ede75b957e879e9770cdde2e1bbc1ef75d45fe555f1ff6ac296a2f648eeee59c7c08260226ea333c285bcf37a9bbfa57ba2ab8083c4be6fc2ebe279537d22da96a07392908cf22b233337a74fe5c603b51712b43c3ee55010ee3d44dd9ba82bba3145ec358f863e04bbfa53799a7a9216718fd5859da2f0deb77b8e315ad6868fdec9400f45a48e6dc8ddbaeb3'),
|
||||
packet.to_bytes())
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
def test_process_onion_packet_mixed_payloads(self):
|
||||
# this test is not from bolt-04, but is based on the one there;
|
||||
# here the TLV payloads are actually sane...
|
||||
payment_path_pubkeys = [
|
||||
bfh('02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619'),
|
||||
bfh('0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c'),
|
||||
bfh('027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007'),
|
||||
bfh('032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'),
|
||||
bfh('02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145'),
|
||||
]
|
||||
payment_path_privkeys = [
|
||||
bfh('4141414141414141414141414141414141414141414141414141414141414141'),
|
||||
bfh('4242424242424242424242424242424242424242424242424242424242424242'),
|
||||
bfh('4343434343434343434343434343434343434343434343434343434343434343'),
|
||||
bfh('4444444444444444444444444444444444444444444444444444444444444444'),
|
||||
bfh('4545454545454545454545454545454545454545454545454545454545454545'),
|
||||
]
|
||||
session_key = bfh('4141414141414141414141414141414141414141414141414141414141414141')
|
||||
associated_data = bfh('4242424242424242424242424242424242424242424242424242424242424242')
|
||||
hops_data = [
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 0},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 0},
|
||||
"short_channel_id": {"short_channel_id": bfh('0000000000000000')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=True, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 1},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 1},
|
||||
"short_channel_id": {"short_channel_id": bfh('0101010101010101')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=True, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 2},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 2},
|
||||
"short_channel_id": {"short_channel_id": bfh('0202020202020202')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=True, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 3},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 3},
|
||||
"short_channel_id": {"short_channel_id": bfh('0303030303030303')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 4},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 4},
|
||||
"short_channel_id": {"short_channel_id": bfh('0404040404040404')},
|
||||
}),
|
||||
]
|
||||
packet = new_onion_packet(payment_path_pubkeys, session_key, hops_data, associated_data)
|
||||
self.assertEqual(bfh('0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619e5f14350c2a76fc232b5e46d421e9615471ab9e0bc887beff8c95fdb878f7b3a71bde5adfa90b337f34616d8673d09dd055937273045566ce537ffbe3f9d1f263dc10c7d61ae590536c609010079a232a247922a5395359a63dfbefb85f40317e23254f3023f7d4a98f746c9ab06647645ce55c67308e3c77dc87a1caeac51b03b23c60f05e536e1d757c8c1093e34accfc4f97b5920f6dd2069d5b9ddbb384c3ac575e999a92a4434470ab0aa040c4c3cace3162a405842a88be783e64fad54bd6727c23fc446b7ec0dc3eec5a03eb6c70ec2784911c9e6d274322ec465f0972eb8e771b149f319582ba64dbc2b8e56a3ea79002801c09354f1541cf79bd1dccf5d6bd6b6bacc87a0f24ce497e14e8037e5a79fb4d9ca63fe47f17765963e8f17468a5eaec19a6cca2bfc4e4a366fea3a92112a945856be55e45197ecbab523025e7589529c30cc8addc8fa39d23ef64fa2e51a219c3bd4d3c484832f8e5af16bc46cdba0403991f4fc1b74beef857acf15fefed82ac8678ca66d26262c681beddfdb485aa498813b1a6c5833f1339c1a35244ab76baa0ccaf681ec1f54004e387063335648a77b65d90dde74f1c4b0a729ca25fa53256f7db6d35818b4e5910ba78ec69cf3646bf248ef46cf9cc33062662de2afe4dcf005951b85fd759429fa1ae490b78b14132ccb791232a6c680f03634c0136817f51bf9603a0dba405e7b347830be4327fceccd4734456842b82cf6275393b279bc6ac93d743e00a2d6042960089f70c782ce554b9f73eeeefeea50df7f6f80de1c4e869a7b502f9a5df30d1175402fa780812d35c6d489a30bb0cea53a1088669a238cccf416ecb37f8d8e6ea1327b64979d48e937db69a44a902923a75113685a4aca4a8d9c62b388b48d9c9e2ab9c2df4d529223144de6e16f2dd95a063da79163b3fe006a80263cde4410648f7c3e1f4a7707f82eb0e209002d972c7e57b4ff8ce063fa7b4140f52f569f0cc8793a97a170613efb6b27ba3a0370f8ea74fc0d6aabba54e0ee967abc70e87b580d2aac244236b7752db9d83b159afc1faf6b44b697643235bf59e99f43428caff409d26b9139538865b1f5cf4699f9296088aca461209024ad1dd00e3566e4fde2117b7b3ffced6696b735816a00199890056de86dcbb1b930228143dbf04f07c0eb34370089ea55c43b2c4546cbe1ff0c3a6217d994af9b4225f4b5acb1e3129f5f5b98d381a4692a8561c670b2ee95869f9614e76bb07f623c5194e1c9d26334026f8f5437ec1cde526f914fa094a465f0adcea32b79bfa44d2562536b0d8366da9ee577666c1d5e39615444ca5c900b8199fafac002b8235688eaa0c6887475a913b37d9a4ed43a894ea4576102e5d475ae0b962240ea95fc367b7ac214a4f8682448a9c0d2eea35727bdedc235a975ecc8148a5b03d6291a051dbefe19c8b344d2713c6664dd94ced53c6be39a837fbf1169cca6a12b0a2710f443ba1afeecb51e94236b2a6ed1c2f365b595443b1515de86dcb8c67282807789b47c331cde2fdd721262bef165fa96b7919d11bc5f2022f5affffdd747c7dbe3de8add829a0a8913519fdf7dba4e8a7a25456d2d559746d39ea6ffa31c7b904792fb734bba30f2e1adf7457a994513a1807785fe7b22bf419d1f407f8e2db8b22c0512b078c0cfdfd599e6c4a9d0cc624b9e24b87f30541c3248cd6643df15d251775cc457df4ea6b4e4c5990d87541028c6f0eb28502db1c11a92797168d0b68cb0a0d345b3a3ad05fc4016862f403c64670c41a2c0c6d4e384f5f7da6a204a24530a51182fd7164f120e74a78decb1ab6cda6b9cfc68ac0a35f7a57e750ead65a8e0429cc16e733b9e4feaea25d06c1a4768'),
|
||||
packet.to_bytes())
|
||||
for i, privkey in enumerate(payment_path_privkeys):
|
||||
processed_packet = process_onion_packet(packet, associated_data, privkey)
|
||||
self.assertEqual(hops_data[i].to_bytes(), processed_packet.hop_data.to_bytes())
|
||||
packet = processed_packet.next_packet
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
def test_process_onion_packet_legacy(self):
|
||||
# this test is not from bolt-04, but is based on the one there;
|
||||
# except here we have the privkeys for these pubkeys
|
||||
payment_path_pubkeys = [
|
||||
|
@ -164,28 +263,38 @@ class Test_LNRouter(TestCaseForTestnet):
|
|||
session_key = bfh('4141414141414141414141414141414141414141414141414141414141414141')
|
||||
associated_data = bfh('4242424242424242424242424242424242424242424242424242424242424242')
|
||||
hops_data = [
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0000000000000000'), bfh('0000000000000000'), bfh('00000000')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0101010101010101'), bfh('0000000000000001'), bfh('00000001')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0202020202020202'), bfh('0000000000000002'), bfh('00000002')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0303030303030303'), bfh('0000000000000003'), bfh('00000003')
|
||||
)),
|
||||
OnionHopsDataSingle(OnionPerHop(
|
||||
bfh('0404040404040404'), bfh('0000000000000004'), bfh('00000004')
|
||||
)),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 0},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 0},
|
||||
"short_channel_id": {"short_channel_id": bfh('0000000000000000')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 1},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 1},
|
||||
"short_channel_id": {"short_channel_id": bfh('0101010101010101')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 2},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 2},
|
||||
"short_channel_id": {"short_channel_id": bfh('0202020202020202')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 3},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 3},
|
||||
"short_channel_id": {"short_channel_id": bfh('0303030303030303')},
|
||||
}),
|
||||
OnionHopsDataSingle(is_tlv_payload=False, payload={
|
||||
"amt_to_forward": {"amt_to_forward": 4},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": 4},
|
||||
"short_channel_id": {"short_channel_id": bfh('0404040404040404')},
|
||||
}),
|
||||
]
|
||||
packet = new_onion_packet(payment_path_pubkeys, session_key, hops_data, associated_data)
|
||||
self.assertEqual(bfh('0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f28368661954176cd9869da33d713aa219fcef1e5c806fef11e696bcc66844de8271c27974a049d041ffc5be934b8575c6ff4371f2f88d4edfd73e445534d3f6ae15b64b0d8308390bebf8d149002e31bdc283056477ba27c8054c248ad7306de31663a7c99ec65b251704041f7c4cc40a0016ba172fbf805ec59132a65a4c7eb1f41337931c5df0f840704535729262d30c6132d1b390f073edec8fa057176c6268b6ad06a82ff0229c3be444ee50b40686bc1306838b93c65771de1b6ca05dace1ff9814a6e58b2dd71e8244c83e28b2ed5a3b09e9e7df5c8c747e5765ba366a4f7407a6c6b0a32fb5521cce7cd668f7434c909c1be027d8595d85893e5f612c49a93eeeed80a78bab9c4a621ce0f6f5df7d64a9c8d435db19de192d9db522c7f7b4e201fc1b61a9bd3efd062ae24455d463818b01e2756c7d0691bc3ac4c017be34c9a8b2913bb1b94056bf7a21730afc3f254ffa41ca140a5d87ff470f536d08619e8004d50de2fe5954d6aa4a00570da397ba15ae9ea4d7d1f136256a9093f0a787a36cbb3520b6a3cf4d1b13b16bf399c4b0326da1382a90bd79cf92f4808c8c84eaa50a8ccf44acbde0e35b2e6b72858c8446d6a05f3ba70fb4adc70af27cea9bd1dc1ea35fb3cc236b8b9b69b614903db339b22ad5dc2ddda7ac65fd7de24e60b7dbba7aafc9d26c0f9fcb03f1bb85dfc21762f862620651db52ea703ae60aa7e07febf11caa95c4245a4b37eb9c233e1ab1604fb85849e7f49cb9f7c681c4d91b7c320eb4b89b9c6bcb636ceadda59f6ed47aa5b1ea0a946ea98f6ad2614e79e0d4ef96d6d65903adb0479709e03008bbdf3355dd87df7d68965fdf1ad5c68b6dc2761b96b10f8eb4c0329a646bf38cf4702002e61565231b4ac7a9cde63d23f7b24c9d42797b3c434558d71ed8bf9fcab2c2aee3e8b38c19f9edc3ad3dfe9ebba7387ce4764f97ed1c1a83552dff8315546761479a6f929c39bcca0891d4a967d1b77fa80feed6ae74ac82ed5fb7be225c3f2b0ebdc652afc2255c47bc318ac645bbf19c0819ff527ff6708a78e19c8ca3dc8087035e10d5ac976e84b71148586c8a5a7b26ed11b5b401ce7bb2ac532207eaa24d2f53aaa8024607da764d807c91489e82fcad04e6b8992a507119367f576ee5ffe6807d5723d60234d4c3f94adce0acfed9dba535ca375446a4e9b500b74ad2a66e1c6b0fc38933f282d3a4a877bceceeca52b46e731ca51a9534224a883c4a45587f973f73a22069a4154b1da03d307d8575c821bef0eef87165b9a1bbf902ecfca82ddd805d10fbb7147b496f6772f01e9bf542b00288f3a6efab32590c1f34535ece03a0587ca187d27a98d4c9aa7c044794baa43a81abbe307f51d0bda6e7b4cf62c4be553b176321777e7fd483d6cec16df137293aaf3ad53608e1c7831368675bb9608db04d5c859e7714edab3d2389837fa071f0795adfabc51507b1adbadc7f83e80bd4e4eb9ed1a89c9e0a6dc16f38d55181d5666b02150651961aab34faef97d80fa4e1960864dfec3b687fd4eadf7aa6c709cb4698ae86ae112f386f33731d996b9d41926a2e820c6ba483a61674a4bae03af37e872ffdc0a9a8a034327af17e13e9e7ac619c9188c2a5c12a6ebf887721455c0e2822e67a621ed49f1f50dfc38b71c29d0224954e84ced086c80de552cca3a14adbe43035901225bafc3db3b672c780e4fa12b59221f93690527efc16a28e7c63d1a99fc881f023b03a157076a7e999a715ed37521adb483e2477d75ba5a55d4abad22b024c5317334b6544f15971591c774d896229e4e668fc1c7958fbd76fa0b152a6f14c95692083badd066b6621367fd73d88ba8d860566e6d55b871d80c68296b80ae8847d'),
|
||||
packet.to_bytes())
|
||||
for i, privkey in enumerate(payment_path_privkeys):
|
||||
processed_packet = process_onion_packet(packet, associated_data, privkey)
|
||||
self.assertEqual(hops_data[i].per_hop.to_bytes(), processed_packet.hop_data.per_hop.to_bytes())
|
||||
self.assertEqual(hops_data[i].to_bytes(), processed_packet.hop_data.to_bytes())
|
||||
packet = processed_packet.next_packet
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
|
|
Loading…
Add table
Reference in a new issue