Browse Source

lnpeer: warnings for shutdown and open_channel

master
bitromortac 4 years ago committed by ThomasV
parent
commit
3915045067
  1. 10
      electrum/lnchannel.py
  2. 27
      electrum/lnpeer.py
  3. 1
      electrum/lnutil.py
  4. 5
      electrum/tests/test_lnpeer.py

10
electrum/lnchannel.py

@ -53,7 +53,7 @@ from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKey
ShortChannelID, map_htlcs_to_ctx_output_idxs, LNPeerAddr, ShortChannelID, map_htlcs_to_ctx_output_idxs, LNPeerAddr,
fee_for_htlc_output, offered_htlc_trim_threshold_sat, fee_for_htlc_output, offered_htlc_trim_threshold_sat,
received_htlc_trim_threshold_sat, make_commitment_output_to_remote_address, received_htlc_trim_threshold_sat, make_commitment_output_to_remote_address,
ChannelType) ChannelType, LNProtocolWarning)
from .lnsweep import create_sweeptxs_for_our_ctx, create_sweeptxs_for_their_ctx from .lnsweep import create_sweeptxs_for_our_ctx, create_sweeptxs_for_their_ctx
from .lnsweep import create_sweeptx_for_their_revoked_htlc, SweepInfo from .lnsweep import create_sweeptx_for_their_revoked_htlc, SweepInfo
from .lnhtlc import HTLCManager from .lnhtlc import HTLCManager
@ -981,7 +981,9 @@ class Channel(AbstractChannel):
preimage_hex = pending_local_commitment.serialize_preimage(0) preimage_hex = pending_local_commitment.serialize_preimage(0)
pre_hash = sha256d(bfh(preimage_hex)) pre_hash = sha256d(bfh(preimage_hex))
if not ecc.verify_signature(self.config[REMOTE].multisig_key.pubkey, sig, pre_hash): if not ecc.verify_signature(self.config[REMOTE].multisig_key.pubkey, sig, pre_hash):
raise Exception(f'failed verifying signature of our updated commitment transaction: {bh2u(sig)} preimage is {preimage_hex}') raise LNProtocolWarning(
f'failed verifying signature of our updated commitment transaction: '
f'{bh2u(sig)} preimage is {preimage_hex}, rawtx: {pending_local_commitment.serialize()}')
htlc_sigs_string = b''.join(htlc_sigs) htlc_sigs_string = b''.join(htlc_sigs)
@ -993,7 +995,7 @@ class Channel(AbstractChannel):
subject=LOCAL, subject=LOCAL,
ctn=next_local_ctn) ctn=next_local_ctn)
if len(htlc_to_ctx_output_idx_map) != len(htlc_sigs): if len(htlc_to_ctx_output_idx_map) != len(htlc_sigs):
raise Exception(f'htlc sigs failure. recv {len(htlc_sigs)} sigs, expected {len(htlc_to_ctx_output_idx_map)}') raise LNProtocolWarning(f'htlc sigs failure. recv {len(htlc_sigs)} sigs, expected {len(htlc_to_ctx_output_idx_map)}')
for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items(): for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
htlc_sig = htlc_sigs[htlc_relative_idx] htlc_sig = htlc_sigs[htlc_relative_idx]
self._verify_htlc_sig(htlc=htlc, self._verify_htlc_sig(htlc=htlc,
@ -1021,7 +1023,7 @@ class Channel(AbstractChannel):
pre_hash = sha256d(bfh(htlc_tx.serialize_preimage(0))) pre_hash = sha256d(bfh(htlc_tx.serialize_preimage(0)))
remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, pcp) remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, pcp)
if not ecc.verify_signature(remote_htlc_pubkey, htlc_sig, pre_hash): if not ecc.verify_signature(remote_htlc_pubkey, htlc_sig, pre_hash):
raise Exception(f'failed verifying HTLC signatures: {htlc} {htlc_direction}') raise LNProtocolWarning(f'failed verifying HTLC signatures: {htlc} {htlc_direction}, rawtx: {htlc_tx.serialize()}')
def get_remote_htlc_sig_for_htlc(self, *, htlc_relative_idx: int) -> bytes: def get_remote_htlc_sig_for_htlc(self, *, htlc_relative_idx: int) -> bytes:
data = self.config[LOCAL].current_htlc_signatures data = self.config[LOCAL].current_htlc_signatures

27
electrum/lnpeer.py

@ -42,7 +42,7 @@ from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConf
LightningPeerConnectionClosed, HandshakeFailed, LightningPeerConnectionClosed, HandshakeFailed,
RemoteMisbehaving, ShortChannelID, RemoteMisbehaving, ShortChannelID,
IncompatibleLightningFeatures, derive_payment_secret_from_payment_preimage, IncompatibleLightningFeatures, derive_payment_secret_from_payment_preimage,
UpfrontShutdownScriptViolation, ChannelType) ChannelType, LNProtocolWarning)
from .lnutil import FeeUpdate, channel_id_from_funding_tx from .lnutil import FeeUpdate, channel_id_from_funding_tx
from .lntransport import LNTransport, LNTransportBase from .lntransport import LNTransport, LNTransportBase
from .lnmsg import encode_msg, decode_msg, UnknownOptionalMsgType from .lnmsg import encode_msg, decode_msg, UnknownOptionalMsgType
@ -861,7 +861,10 @@ class Peer(Logger):
payload = await self.wait_for_message('funding_signed', channel_id) 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, []) try:
chan.receive_new_commitment(remote_sig, [])
except LNProtocolWarning as e:
await self.send_warning(channel_id, message=str(e), close_connection=True)
chan.open_with_first_pcp(remote_per_commitment_point, remote_sig) chan.open_with_first_pcp(remote_per_commitment_point, remote_sig)
chan.set_state(ChannelState.OPENING) chan.set_state(ChannelState.OPENING)
self.lnworker.add_new_channel(chan) self.lnworker.add_new_channel(chan)
@ -1020,7 +1023,10 @@ class Peer(Logger):
if isinstance(self.transport, LNTransport): if isinstance(self.transport, LNTransport):
chan.add_or_update_peer_addr(self.transport.peer_addr) chan.add_or_update_peer_addr(self.transport.peer_addr)
remote_sig = funding_created['signature'] remote_sig = funding_created['signature']
chan.receive_new_commitment(remote_sig, []) try:
chan.receive_new_commitment(remote_sig, [])
except LNProtocolWarning as e:
await self.send_warning(channel_id, message=str(e), close_connection=True)
sig_64, _ = chan.sign_next_commitment() sig_64, _ = chan.sign_next_commitment()
self.send_message('funding_signed', self.send_message('funding_signed',
channel_id=channel_id, channel_id=channel_id,
@ -1868,12 +1874,18 @@ class Peer(Logger):
return txid return txid
async def on_shutdown(self, chan: Channel, payload): async def on_shutdown(self, chan: Channel, payload):
# TODO: A receiving node: if it hasn't received a funding_signed (if it is a
# funder) or a funding_created (if it is a fundee):
# SHOULD send an error and fail the channel.
their_scriptpubkey = payload['scriptpubkey'] their_scriptpubkey = payload['scriptpubkey']
their_upfront_scriptpubkey = chan.config[REMOTE].upfront_shutdown_script their_upfront_scriptpubkey = chan.config[REMOTE].upfront_shutdown_script
# BOLT-02 check if they use the upfront shutdown script they advertized # BOLT-02 check if they use the upfront shutdown script they advertized
if their_upfront_scriptpubkey: if self.is_upfront_shutdown_script() and their_upfront_scriptpubkey:
if not (their_scriptpubkey == their_upfront_scriptpubkey): if not (their_scriptpubkey == their_upfront_scriptpubkey):
raise UpfrontShutdownScriptViolation("remote didn't use upfront shutdown script it commited to in channel opening") await self.send_warning(
chan.channel_id,
"remote didn't use upfront shutdown script it commited to in channel opening",
close_connection=True)
else: else:
# BOLT-02 restrict the scriptpubkey to some templates: # BOLT-02 restrict the scriptpubkey to some templates:
if self.is_shutdown_anysegwit() and match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_ANYSEGWIT): if self.is_shutdown_anysegwit() and match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_ANYSEGWIT):
@ -1881,7 +1893,10 @@ class Peer(Logger):
elif match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_WITNESS_V0): elif match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_WITNESS_V0):
pass pass
else: else:
raise Exception(f'scriptpubkey in received shutdown message does not conform to any template: {their_scriptpubkey.hex()}') await self.send_warning(
chan.channel_id,
f'scriptpubkey in received shutdown message does not conform to any template: {their_scriptpubkey.hex()}',
close_connection=True)
chan_id = chan.channel_id chan_id = chan.channel_id
if chan_id in self.shutdown_received: if chan_id in self.shutdown_received:

1
electrum/lnutil.py

@ -351,7 +351,6 @@ class UnableToDeriveSecret(LightningError): pass
class HandshakeFailed(LightningError): pass class HandshakeFailed(LightningError): pass
class ConnStringFormatError(LightningError): pass class ConnStringFormatError(LightningError): pass
class RemoteMisbehaving(LightningError): pass class RemoteMisbehaving(LightningError): pass
class UpfrontShutdownScriptViolation(RemoteMisbehaving): pass
class NotFoundChanAnnouncementForUpdate(Exception): pass class NotFoundChanAnnouncementForUpdate(Exception): pass
class InvalidGossipMsg(Exception): class InvalidGossipMsg(Exception):

5
electrum/tests/test_lnpeer.py

@ -22,9 +22,8 @@ from electrum import simple_config, lnutil
from electrum.lnaddr import lnencode, LnAddr, lndecode from electrum.lnaddr import lnencode, LnAddr, lndecode
from electrum.bitcoin import COIN, sha256 from electrum.bitcoin import COIN, sha256
from electrum.util import bh2u, create_and_start_event_loop, NetworkRetryManager, bfh, OldTaskGroup from electrum.util import bh2u, create_and_start_event_loop, NetworkRetryManager, bfh, OldTaskGroup
from electrum.lnpeer import Peer, UpfrontShutdownScriptViolation from electrum.lnpeer import Peer
from electrum.lnutil import LNPeerAddr, Keypair, privkey_to_pubkey from electrum.lnutil import LNPeerAddr, Keypair, privkey_to_pubkey
from electrum.lnutil import LightningPeerConnectionClosed, RemoteMisbehaving
from electrum.lnutil import PaymentFailure, LnFeatures, HTLCOwner from electrum.lnutil import PaymentFailure, LnFeatures, HTLCOwner
from electrum.lnchannel import ChannelState, PeerState, Channel from electrum.lnchannel import ChannelState, PeerState, Channel
from electrum.lnrouter import LNPathFinder, PathEdge, LNPathInconsistent from electrum.lnrouter import LNPathFinder, PathEdge, LNPathInconsistent
@ -1201,7 +1200,7 @@ class TestPeer(TestCaseForTestnet):
gath = asyncio.gather(*coros) gath = asyncio.gather(*coros)
await gath await gath
with self.assertRaises(UpfrontShutdownScriptViolation): with self.assertRaises(GracefulDisconnect):
run(test()) run(test())
# bob sends the same upfront_shutdown_script has he announced # bob sends the same upfront_shutdown_script has he announced

Loading…
Cancel
Save