thandle_error_code_from_failed_htlc: omg brainfart :( - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 10c21834613318f15bd30213fa52eaada50d1285 DIR parent c2111a2616d2e9b759ce84304ad61d4b90f3db8f HTML Author: SomberNight <somber.night@protonmail.com> Date: Wed, 24 Jun 2020 21:33:44 +0200 handle_error_code_from_failed_htlc: omg brainfart :( follow-up 85841ba20de7cb6459066538474d3f13e010031e Diffstat: M electrum/lnworker.py | 41 +++++++++++++++++++------------ M electrum/tests/test_lnutil.py | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 16 deletions(-) --- DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py t@@ -7,7 +7,7 @@ import os from decimal import Decimal import random import time -from typing import Optional, Sequence, Tuple, List, Dict, TYPE_CHECKING, NamedTuple, Union, Mapping +from typing import Optional, Sequence, Tuple, List, Dict, TYPE_CHECKING, NamedTuple, Union, Mapping, Any import threading import socket import aiohttp t@@ -968,21 +968,10 @@ class LNWallet(LNWorker): offset = failure_codes[code] channel_update_len = int.from_bytes(data[offset:offset+2], byteorder="big") channel_update_as_received = data[offset+2: offset+2+channel_update_len] - channel_update_typed = (258).to_bytes(length=2, byteorder="big") + channel_update_as_received - # note: some nodes put channel updates in error msgs with the leading msg_type already there. - # we try decoding both ways here. - try: - message_type, payload = decode_msg(channel_update_typed) - if not payload['chain_hash'] != constants.net.rev_genesis_bytes(): raise Exception() - payload['raw'] = channel_update_typed - except: # FIXME: too broad - try: - message_type, payload = decode_msg(channel_update_as_received) - if not payload['chain_hash'] != constants.net.rev_genesis_bytes(): raise Exception() - payload['raw'] = channel_update_as_received - except: - self.logger.info(f'could not decode channel_update for failed htlc: {channel_update_as_received.hex()}') - return True + payload = self._decode_channel_update_msg(channel_update_as_received) + if payload is None: + self.logger.info(f'could not decode channel_update for failed htlc: {channel_update_as_received.hex()}') + return True r = self.channel_db.add_channel_update(payload) blacklist = False short_channel_id = ShortChannelID(payload['short_channel_id']) t@@ -1005,6 +994,26 @@ class LNWallet(LNWorker): blacklist = True return blacklist + @classmethod + def _decode_channel_update_msg(cls, chan_upd_msg: bytes) -> Optional[Dict[str, Any]]: + channel_update_as_received = chan_upd_msg + channel_update_typed = (258).to_bytes(length=2, byteorder="big") + channel_update_as_received + # note: some nodes put channel updates in error msgs with the leading msg_type already there. + # we try decoding both ways here. + try: + message_type, payload = decode_msg(channel_update_typed) + if payload['chain_hash'] != constants.net.rev_genesis_bytes(): raise Exception() + payload['raw'] = channel_update_typed + return payload + except: # FIXME: too broad + try: + message_type, payload = decode_msg(channel_update_as_received) + if payload['chain_hash'] != constants.net.rev_genesis_bytes(): raise Exception() + payload['raw'] = channel_update_as_received + return payload + except: + return None + @staticmethod def _check_invoice(invoice: str, *, amount_msat: int = None) -> LnAddr: addr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP) DIR diff --git a/electrum/tests/test_lnutil.py b/electrum/tests/test_lnutil.py t@@ -11,6 +11,7 @@ from electrum.lnutil import (RevocationStore, get_per_commitment_secret_from_see ScriptHtlc, extract_nodeid, calc_fees_for_commitment_tx, UpdateAddHtlc, LnFeatures) from electrum.util import bh2u, bfh, MyEncoder from electrum.transaction import Transaction, PartialTransaction +from electrum.lnworker import LNWallet from . import ElectrumTestCase t@@ -805,3 +806,23 @@ class TestLNUtil(ElectrumTestCase): features.for_invoice()) features = LnFeatures.BASIC_MPP_OPT | LnFeatures.PAYMENT_SECRET_REQ | LnFeatures.VAR_ONION_REQ self.assertEqual(features, features.for_invoice()) + + def test_lnworker_decode_channel_update_msg(self): + msg_without_prefix = bytes.fromhex("439b71c8ddeff63004e4ff1f9764a57dcf20232b79d9d669aef0e31c42be8e44208f7d868d0133acb334047f30e9399dece226ccd98e5df5330adf7f356290516fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008762700054a00005ef2cf9c0101009000000000000003e80000000000000001000000002367b880") + # good messages + self.assertNotEqual( + None, + LNWallet._decode_channel_update_msg(msg_without_prefix)) + self.assertNotEqual( + None, + LNWallet._decode_channel_update_msg(bytes.fromhex("0102") + msg_without_prefix)) + # bad messages + self.assertEqual( + None, + LNWallet._decode_channel_update_msg(bytes.fromhex("0102030405"))) + self.assertEqual( + None, + LNWallet._decode_channel_update_msg(bytes.fromhex("ffff") + msg_without_prefix)) + self.assertEqual( + None, + LNWallet._decode_channel_update_msg(bytes.fromhex("0101") + msg_without_prefix))