tln chan verifier: fix code rot - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 24ebc77d76cb342502a8443c4edc2a1feff8f067 DIR parent 0ab88b821c702b6a424bae8508ce1a9e30b7fa59 HTML Author: SomberNight <somber.night@protonmail.com> Date: Tue, 10 Dec 2019 01:14:38 +0100 ln chan verifier: fix code rot Diffstat: M electrum/channel_db.py | 30 ++++++++++++++++++++++-------- M electrum/interface.py | 9 ++++----- M electrum/lnverifier.py | 22 ++++++++++------------ 3 files changed, 36 insertions(+), 25 deletions(-) --- DIR diff --git a/electrum/channel_db.py b/electrum/channel_db.py t@@ -295,7 +295,12 @@ class ChannelDB(SqlDB): for node_id in list(self._addresses.keys())[-self.NUM_MAX_RECENT_PEERS:]] return list(reversed(r)) - def add_channel_announcement(self, msg_payloads, trusted=True): + # note: currently channel announcements are trusted by default (trusted=True); + # they are not verified. Verifying them would make the gossip sync + # even slower; especially as servers will start throttling us. + # It would probably put significant strain on servers if all clients + # verified the complete gossip. + def add_channel_announcement(self, msg_payloads, *, trusted=True): if type(msg_payloads) is dict: msg_payloads = [msg_payloads] added = 0 t@@ -311,17 +316,26 @@ class ChannelDB(SqlDB): except UnknownEvenFeatureBits: self.logger.info("unknown feature bits") continue - added += 1 - self._channels[short_channel_id] = channel_info - self._channels_for_node[channel_info.node1_id].add(channel_info.short_channel_id) - self._channels_for_node[channel_info.node2_id].add(channel_info.short_channel_id) - self.save_channel(channel_info) - if not trusted: - self.ca_verifier.add_new_channel_info(channel_info.short_channel_id, msg) + if trusted: + added += 1 + self.add_verified_channel_info(msg) + else: + added += self.ca_verifier.add_new_channel_info(short_channel_id, msg) self.update_counts() self.logger.debug('add_channel_announcement: %d/%d'%(added, len(msg_payloads))) + def add_verified_channel_info(self, msg: dict, *, capacity_sat: int = None) -> None: + try: + channel_info = ChannelInfo.from_msg(msg) + except UnknownEvenFeatureBits: + return + channel_info = channel_info._replace(capacity_sat=capacity_sat) + self._channels[channel_info.short_channel_id] = channel_info + self._channels_for_node[channel_info.node1_id].add(channel_info.short_channel_id) + self._channels_for_node[channel_info.node2_id].add(channel_info.short_channel_id) + self.save_channel(channel_info) + def print_change(self, old_policy: Policy, new_policy: Policy): # print what changed between policies if old_policy.cltv_expiry_delta != new_policy.cltv_expiry_delta: DIR diff --git a/electrum/interface.py b/electrum/interface.py t@@ -29,7 +29,7 @@ import sys import traceback import asyncio import socket -from typing import Tuple, Union, List, TYPE_CHECKING, Optional +from typing import Tuple, Union, List, TYPE_CHECKING, Optional, Set from collections import defaultdict from ipaddress import IPv4Network, IPv6Network, ip_address, IPv6Address import itertools t@@ -233,7 +233,7 @@ class Interface(Logger): assert network.config.path self.cert_path = _get_cert_path_for_host(config=network.config, host=self.host) self.blockchain = None # type: Optional[Blockchain] - self._requested_chunks = set() + self._requested_chunks = set() # type: Set[int] self.network = network self._set_proxy(proxy) self.session = None # type: Optional[NotificationSession] t@@ -431,7 +431,7 @@ class Interface(Logger): res = await self.session.send_request('blockchain.block.header', [height], timeout=timeout) return blockchain.deserialize_header(bytes.fromhex(res), height) - async def request_chunk(self, height, tip=None, *, can_return_early=False): + async def request_chunk(self, height: int, tip=None, *, can_return_early=False): index = height // 2016 if can_return_early and index in self._requested_chunks: return t@@ -444,8 +444,7 @@ class Interface(Logger): self._requested_chunks.add(index) res = await self.session.send_request('blockchain.block.headers', [index * 2016, size]) finally: - try: self._requested_chunks.remove(index) - except KeyError: pass + self._requested_chunks.discard(index) conn = self.blockchain.connect_chunk(index, res['hex']) if not conn: return conn, 0 DIR diff --git a/electrum/lnverifier.py b/electrum/lnverifier.py t@@ -55,7 +55,7 @@ class LNChannelVerifier(NetworkJobOnDefaultServer): def __init__(self, network: 'Network', channel_db: 'ChannelDB'): self.channel_db = channel_db self.lock = threading.Lock() - self.unverified_channel_info = {} # type: Dict[ShortChannelID, dict] # scid -> msg_payload + self.unverified_channel_info = {} # type: Dict[ShortChannelID, dict] # scid -> msg_dict # channel announcements that seem to be invalid: self.blacklist = set() # type: Set[ShortChannelID] NetworkJobOnDefaultServer.__init__(self, network) t@@ -65,13 +65,14 @@ class LNChannelVerifier(NetworkJobOnDefaultServer): self.started_verifying_channel = set() # type: Set[ShortChannelID] # TODO make async; and rm self.lock completely - def add_new_channel_info(self, short_channel_id: ShortChannelID, msg_payload): + def add_new_channel_info(self, short_channel_id: ShortChannelID, msg: dict) -> bool: if short_channel_id in self.unverified_channel_info: - return + return False if short_channel_id in self.blacklist: - return + return False with self.lock: - self.unverified_channel_info[short_channel_id] = msg_payload + self.unverified_channel_info[short_channel_id] = msg + return True async def _start_tasks(self): async with self.group as group: t@@ -146,10 +147,8 @@ class LNChannelVerifier(NetworkJobOnDefaultServer): self.logger.info(f"received tx does not match expected txid ({tx_hash} != {tx.txid()})") return # check funding output - msg_payload = self.unverified_channel_info[short_channel_id] - msg_type, chan_ann = decode_msg(msg_payload) - assert msg_type == 'channel_announcement' - redeem_script = funding_output_script_from_keys(chan_ann['bitcoin_key_1'], chan_ann['bitcoin_key_2']) + chan_ann_msg = self.unverified_channel_info[short_channel_id] + redeem_script = funding_output_script_from_keys(chan_ann_msg['bitcoin_key_1'], chan_ann_msg['bitcoin_key_2']) expected_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script) try: actual_output = tx.outputs()[short_channel_id.output_index] t@@ -162,14 +161,13 @@ class LNChannelVerifier(NetworkJobOnDefaultServer): self._remove_channel_from_unverified_db(short_channel_id) return # put channel into channel DB - self.channel_db.add_verified_channel_info(short_channel_id, actual_output.value) + self.channel_db.add_verified_channel_info(chan_ann_msg, capacity_sat=actual_output.value) self._remove_channel_from_unverified_db(short_channel_id) def _remove_channel_from_unverified_db(self, short_channel_id: ShortChannelID): with self.lock: self.unverified_channel_info.pop(short_channel_id, None) - try: self.started_verifying_channel.remove(short_channel_id) - except KeyError: pass + self.started_verifying_channel.discard(short_channel_id) def _blacklist_short_channel_id(self, short_channel_id: ShortChannelID) -> None: self.blacklist.add(short_channel_id)