mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 17:31:36 +00:00
lnpeer: Use a single queue per channel for messages that are ordered.
Forward error messages with 'temporary_channel_id' to the correct channel_id
This commit is contained in:
parent
28c5825f41
commit
ac884eb3c3
1 changed files with 35 additions and 59 deletions
|
@ -82,15 +82,11 @@ class Peer(Logger):
|
||||||
self.reply_channel_range = asyncio.Queue()
|
self.reply_channel_range = asyncio.Queue()
|
||||||
# gossip uses a single queue to preserve message order
|
# gossip uses a single queue to preserve message order
|
||||||
self.gossip_queue = asyncio.Queue()
|
self.gossip_queue = asyncio.Queue()
|
||||||
# channel messsage queues
|
self.ordered_messages = ['accept_channel', 'funding_signed', 'funding_created', 'accept_channel', 'channel_reestablish', 'closing_signed']
|
||||||
|
self.ordered_message_queues = defaultdict(asyncio.Queue) # for messsage that are ordered
|
||||||
|
self.temp_id_to_id = {} # to forward error messages
|
||||||
self.shutdown_received = defaultdict(asyncio.Future)
|
self.shutdown_received = defaultdict(asyncio.Future)
|
||||||
self.channel_accepted = defaultdict(asyncio.Queue)
|
|
||||||
self.channel_reestablished = defaultdict(asyncio.Queue)
|
|
||||||
self.funding_signed = defaultdict(asyncio.Queue)
|
|
||||||
self.funding_created = defaultdict(asyncio.Queue)
|
|
||||||
self.announcement_signatures = defaultdict(asyncio.Queue)
|
self.announcement_signatures = defaultdict(asyncio.Queue)
|
||||||
self.closing_signed = defaultdict(asyncio.Queue)
|
|
||||||
#
|
|
||||||
self.orphan_channel_updates = OrderedDict()
|
self.orphan_channel_updates = OrderedDict()
|
||||||
self._local_changed_events = defaultdict(asyncio.Event)
|
self._local_changed_events = defaultdict(asyncio.Event)
|
||||||
self._remote_changed_events = defaultdict(asyncio.Event)
|
self._remote_changed_events = defaultdict(asyncio.Event)
|
||||||
|
@ -147,26 +143,28 @@ class Peer(Logger):
|
||||||
|
|
||||||
def process_message(self, message):
|
def process_message(self, message):
|
||||||
message_type, payload = decode_msg(message)
|
message_type, payload = decode_msg(message)
|
||||||
try:
|
if message_type in self.ordered_messages:
|
||||||
f = getattr(self, 'on_' + message_type)
|
chan_id = payload.get('channel_id') or payload["temporary_channel_id"]
|
||||||
except AttributeError:
|
self.ordered_message_queues[chan_id].put_nowait((message_type, payload))
|
||||||
#self.logger.info("Received '%s'" % message_type.upper(), payload)
|
else:
|
||||||
return
|
try:
|
||||||
# raw message is needed to check signature
|
f = getattr(self, 'on_' + message_type)
|
||||||
if message_type in ['node_announcement', 'channel_announcement', 'channel_update']:
|
except AttributeError:
|
||||||
payload['raw'] = message
|
#self.logger.info("Received '%s'" % message_type.upper(), payload)
|
||||||
execution_result = f(payload)
|
return
|
||||||
if asyncio.iscoroutinefunction(f):
|
# raw message is needed to check signature
|
||||||
asyncio.ensure_future(execution_result)
|
if message_type in ['node_announcement', 'channel_announcement', 'channel_update']:
|
||||||
|
payload['raw'] = message
|
||||||
|
execution_result = f(payload)
|
||||||
|
if asyncio.iscoroutinefunction(f):
|
||||||
|
asyncio.ensure_future(execution_result)
|
||||||
|
|
||||||
def on_error(self, payload):
|
def on_error(self, payload):
|
||||||
self.logger.info(f"on_error: {payload['data'].decode('ascii')}")
|
self.logger.info(f"on_error: {payload['data'].decode('ascii')}")
|
||||||
chan_id = payload.get("channel_id")
|
chan_id = payload.get("channel_id")
|
||||||
for d in [ self.channel_accepted, self.funding_signed,
|
if chan_id in self.temp_id_to_id:
|
||||||
self.funding_created, self.channel_reestablished,
|
chan_id = self.temp_id_to_id[chan_id]
|
||||||
self.announcement_signatures, self.closing_signed ]:
|
self.ordered_message_queues[chan_id].put_nowait((None, {'error':payload['data']}))
|
||||||
if chan_id in d:
|
|
||||||
d[chan_id].put_nowait({'error':payload['data']})
|
|
||||||
|
|
||||||
def on_ping(self, payload):
|
def on_ping(self, payload):
|
||||||
l = int.from_bytes(payload['num_pong_bytes'], 'big')
|
l = int.from_bytes(payload['num_pong_bytes'], 'big')
|
||||||
|
@ -175,21 +173,14 @@ class Peer(Logger):
|
||||||
def on_pong(self, payload):
|
def on_pong(self, payload):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_accept_channel(self, payload):
|
async def wait_for_message(self, expected_name, channel_id):
|
||||||
temp_chan_id = payload["temporary_channel_id"]
|
q = self.ordered_message_queues[channel_id]
|
||||||
if temp_chan_id not in self.channel_accepted:
|
name, payload = await asyncio.wait_for(q.get(), LN_P2P_NETWORK_TIMEOUT)
|
||||||
raise Exception("Got unknown accept_channel")
|
if payload.get('error'):
|
||||||
self.channel_accepted[temp_chan_id].put_nowait(payload)
|
raise Exception('Remote peer reported error: ' + repr(payload.get('error')))
|
||||||
|
if name != expected_name:
|
||||||
def on_funding_signed(self, payload):
|
raise Exception(f"Received unexpected '{name}'")
|
||||||
channel_id = payload['channel_id']
|
return payload
|
||||||
if channel_id not in self.funding_signed: raise Exception("Got unknown funding_signed")
|
|
||||||
self.funding_signed[channel_id].put_nowait(payload)
|
|
||||||
|
|
||||||
def on_funding_created(self, payload):
|
|
||||||
channel_id = payload['temporary_channel_id']
|
|
||||||
if channel_id not in self.funding_created: raise Exception("Got unknown funding_created")
|
|
||||||
self.funding_created[channel_id].put_nowait(payload)
|
|
||||||
|
|
||||||
def on_init(self, payload):
|
def on_init(self, payload):
|
||||||
if self._received_init:
|
if self._received_init:
|
||||||
|
@ -533,9 +524,7 @@ class Peer(Logger):
|
||||||
channel_reserve_satoshis=local_config.reserve_sat,
|
channel_reserve_satoshis=local_config.reserve_sat,
|
||||||
htlc_minimum_msat=1,
|
htlc_minimum_msat=1,
|
||||||
)
|
)
|
||||||
payload = await asyncio.wait_for(self.channel_accepted[temp_channel_id].get(), LN_P2P_NETWORK_TIMEOUT)
|
payload = await self.wait_for_message('accept_channel', temp_channel_id)
|
||||||
if payload.get('error'):
|
|
||||||
raise Exception('Remote Lightning peer reported error: ' + repr(payload.get('error')))
|
|
||||||
remote_per_commitment_point = payload['first_per_commitment_point']
|
remote_per_commitment_point = payload['first_per_commitment_point']
|
||||||
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
|
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
|
||||||
if funding_txn_minimum_depth <= 0:
|
if funding_txn_minimum_depth <= 0:
|
||||||
|
@ -601,12 +590,13 @@ class Peer(Logger):
|
||||||
lnworker=self.lnworker,
|
lnworker=self.lnworker,
|
||||||
initial_feerate=feerate)
|
initial_feerate=feerate)
|
||||||
sig_64, _ = chan.sign_next_commitment()
|
sig_64, _ = chan.sign_next_commitment()
|
||||||
|
self.temp_id_to_id[temp_channel_id] = channel_id
|
||||||
self.send_message("funding_created",
|
self.send_message("funding_created",
|
||||||
temporary_channel_id=temp_channel_id,
|
temporary_channel_id=temp_channel_id,
|
||||||
funding_txid=funding_txid_bytes,
|
funding_txid=funding_txid_bytes,
|
||||||
funding_output_index=funding_index,
|
funding_output_index=funding_index,
|
||||||
signature=sig_64)
|
signature=sig_64)
|
||||||
payload = await asyncio.wait_for(self.funding_signed[channel_id].get(), LN_P2P_NETWORK_TIMEOUT)
|
payload = await self.wait_for_message('funding_signed', channel_id)
|
||||||
self.logger.info('received funding_signed')
|
self.logger.info('received funding_signed')
|
||||||
remote_sig = payload['signature']
|
remote_sig = payload['signature']
|
||||||
chan.receive_new_commitment(remote_sig, [])
|
chan.receive_new_commitment(remote_sig, [])
|
||||||
|
@ -666,7 +656,7 @@ class Peer(Logger):
|
||||||
htlc_basepoint=local_config.htlc_basepoint.pubkey,
|
htlc_basepoint=local_config.htlc_basepoint.pubkey,
|
||||||
first_per_commitment_point=per_commitment_point_first,
|
first_per_commitment_point=per_commitment_point_first,
|
||||||
)
|
)
|
||||||
funding_created = await self.funding_created[temp_chan_id].get()
|
funding_created = await self.wait_for_message('funding_created', temp_chan_id)
|
||||||
funding_idx = int.from_bytes(funding_created['funding_output_index'], 'big')
|
funding_idx = int.from_bytes(funding_created['funding_output_index'], 'big')
|
||||||
funding_txid = bh2u(funding_created['funding_txid'][::-1])
|
funding_txid = bh2u(funding_created['funding_txid'][::-1])
|
||||||
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_idx)
|
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_idx)
|
||||||
|
@ -715,14 +705,6 @@ class Peer(Logger):
|
||||||
raise Exception(f'reserve too high: {remote_reserve_sat}, funding_sat: {funding_sat}')
|
raise Exception(f'reserve too high: {remote_reserve_sat}, funding_sat: {funding_sat}')
|
||||||
return remote_reserve_sat
|
return remote_reserve_sat
|
||||||
|
|
||||||
def on_channel_reestablish(self, payload):
|
|
||||||
chan_id = payload["channel_id"]
|
|
||||||
chan = self.channels.get(chan_id)
|
|
||||||
if not chan:
|
|
||||||
self.logger.info(f"Received unknown channel_reestablish {bh2u(chan_id)} {payload}")
|
|
||||||
raise Exception('Unknown channel_reestablish')
|
|
||||||
self.channel_reestablished[chan_id].put_nowait(payload)
|
|
||||||
|
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def reestablish_channel(self, chan: Channel):
|
async def reestablish_channel(self, chan: Channel):
|
||||||
await self.initialized
|
await self.initialized
|
||||||
|
@ -765,8 +747,7 @@ class Peer(Logger):
|
||||||
self.logger.info(f'channel_reestablish: sent channel_reestablish with '
|
self.logger.info(f'channel_reestablish: sent channel_reestablish with '
|
||||||
f'(next_local_ctn={next_local_ctn}, '
|
f'(next_local_ctn={next_local_ctn}, '
|
||||||
f'oldest_unrevoked_remote_ctn={oldest_unrevoked_remote_ctn})')
|
f'oldest_unrevoked_remote_ctn={oldest_unrevoked_remote_ctn})')
|
||||||
|
msg = await self.wait_for_message('channel_reestablish', chan_id)
|
||||||
msg = await self.channel_reestablished[chan_id].get()
|
|
||||||
their_next_local_ctn = int.from_bytes(msg["next_local_commitment_number"], 'big')
|
their_next_local_ctn = int.from_bytes(msg["next_local_commitment_number"], 'big')
|
||||||
their_oldest_unrevoked_remote_ctn = int.from_bytes(msg["next_remote_revocation_number"], 'big')
|
their_oldest_unrevoked_remote_ctn = int.from_bytes(msg["next_remote_revocation_number"], 'big')
|
||||||
their_local_pcp = msg.get("my_current_per_commitment_point")
|
their_local_pcp = msg.get("my_current_per_commitment_point")
|
||||||
|
@ -1356,11 +1337,6 @@ class Peer(Logger):
|
||||||
feerate_per_kw=feerate_per_kw)
|
feerate_per_kw=feerate_per_kw)
|
||||||
await self.await_remote(chan, remote_ctn)
|
await self.await_remote(chan, remote_ctn)
|
||||||
|
|
||||||
def on_closing_signed(self, payload):
|
|
||||||
chan_id = payload["channel_id"]
|
|
||||||
if chan_id not in self.closing_signed: raise Exception("Got unknown closing_signed")
|
|
||||||
self.closing_signed[chan_id].put_nowait(payload)
|
|
||||||
|
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def close_channel(self, chan_id: bytes):
|
async def close_channel(self, chan_id: bytes):
|
||||||
chan = self.channels[chan_id]
|
chan = self.channels[chan_id]
|
||||||
|
@ -1404,7 +1380,7 @@ class Peer(Logger):
|
||||||
our_sig, closing_tx = chan.make_closing_tx(scriptpubkey, payload['scriptpubkey'], fee_sat=our_fee)
|
our_sig, closing_tx = chan.make_closing_tx(scriptpubkey, payload['scriptpubkey'], fee_sat=our_fee)
|
||||||
self.send_message('closing_signed', channel_id=chan.channel_id, fee_satoshis=our_fee, signature=our_sig)
|
self.send_message('closing_signed', channel_id=chan.channel_id, fee_satoshis=our_fee, signature=our_sig)
|
||||||
# FIXME: the remote SHOULD send closing_signed, but some don't.
|
# FIXME: the remote SHOULD send closing_signed, but some don't.
|
||||||
cs_payload = await asyncio.wait_for(self.closing_signed[chan.channel_id].get(), LN_P2P_NETWORK_TIMEOUT)
|
cs_payload = await self.wait_for_message('closing_signed', chan.channel_id)
|
||||||
their_fee = int.from_bytes(cs_payload['fee_satoshis'], 'big')
|
their_fee = int.from_bytes(cs_payload['fee_satoshis'], 'big')
|
||||||
their_sig = cs_payload['signature']
|
their_sig = cs_payload['signature']
|
||||||
if our_fee == their_fee:
|
if our_fee == their_fee:
|
||||||
|
|
Loading…
Add table
Reference in a new issue