tln: announcement reliability fixes for qt, remove asserts forbidding unbalanced channels - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 6f88c55f170774e24623e39c32120cb296602ad6 DIR parent e9fec66eb4d4c8292f86b8869ee794a5d0cfdf1a HTML Author: Janus <ysangkok@gmail.com> Date: Tue, 19 Jun 2018 13:02:59 +0200 ln: announcement reliability fixes for qt, remove asserts forbidding unbalanced channels Diffstat: M lib/lnbase.py | 35 ++++++++++++++++++++++--------- M lib/lnhtlc.py | 12 ++++-------- M lib/lnworker.py | 33 +++++++++++++++---------------- 3 files changed, 45 insertions(+), 35 deletions(-) --- DIR diff --git a/lib/lnbase.py b/lib/lnbase.py t@@ -279,7 +279,7 @@ ChannelConfig = namedtuple("ChannelConfig", [ "to_self_delay", "dust_limit_sat", "max_htlc_value_in_flight_msat", "max_accepted_htlcs"]) OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"]) RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_msat", "revocation_store", "last_per_commitment_point", "next_htlc_id"]) -LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_msat", "next_htlc_id", "funding_locked_received"]) +LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_msat", "next_htlc_id", "funding_locked_received", "was_announced"]) ChannelConstraints = namedtuple("ChannelConstraints", ["feerate", "capacity", "is_initiator", "funding_txn_minimum_depth"]) OpenChannel = namedtuple("OpenChannel", ["channel_id", "short_channel_id", "funding_outpoint", "local_config", "remote_config", "remote_state", "local_state", "constraints", "node_id"]) t@@ -600,7 +600,6 @@ class Peer(PrintError): self.commitment_signed = defaultdict(asyncio.Queue) self.announcement_signatures = defaultdict(asyncio.Queue) self.update_fail_htlc = defaultdict(asyncio.Queue) - self.is_funding_six_deep = defaultdict(lambda: False) self.localfeatures = (0x08 if request_initial_sync else 0) self.nodes = {} self.channels = lnworker.channels t@@ -771,7 +770,11 @@ class Peer(PrintError): def on_announcement_signatures(self, payload): channel_id = payload['channel_id'] - self.announcement_signatures[channel_id].put_nowait(payload) + chan = self.channels[payload['channel_id']] + if chan.local_state.was_announced: + h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan) + else: + self.announcement_signatures[channel_id].put_nowait(payload) @aiosafe async def main_loop(self): t@@ -794,6 +797,7 @@ class Peer(PrintError): self.print_error('closing lnbase') self.writer.close() + @aiosafe async def channel_establishment_flow(self, wallet, config, password, funding_sat, push_msat, temp_channel_id): await self.initialized # see lnd/keychain/derivation.go t@@ -900,7 +904,8 @@ class Peer(PrintError): per_commitment_secret_seed=per_commitment_secret_seed, amount_msat=local_amount, next_htlc_id = 0, - funding_locked_received = False + funding_locked_received = False, + was_announced = False ), constraints=ChannelConstraints(capacity=funding_sat, feerate=local_feerate, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth) ) t@@ -976,10 +981,20 @@ class Peer(PrintError): if chan.short_channel_id: self.mark_open(chan) - async def funding_six_deep(self, chan): - if self.is_funding_six_deep[chan.channel_id]: - return - self.is_funding_six_deep[chan.channel_id] = True + def on_network_update(self, chan, funding_tx_depth): + """ + Only called when the channel is OPEN. + + Runs on the Network thread. + """ + if not chan.local_state.was_announced and funding_tx_depth >= 6: + chan = chan._replace(local_state=chan.local_state._replace(was_announced=True)) + coro = self.handle_announcements(chan) + self.lnworker.save_channel(chan) + asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop) + + @aiosafe + async def handle_announcements(self, chan): h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan) announcement_signatures_msg = await self.announcement_signatures[chan.channel_id].get() remote_node_sig = announcement_signatures_msg["node_signature"] t@@ -1006,7 +1021,7 @@ class Peer(PrintError): bitcoin_signature_1=bitcoin_sigs[0], bitcoin_signature_2=bitcoin_sigs[1], len=0, - #features + #features not set (defaults to zeros) chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)), short_channel_id=chan.short_channel_id, node_id_1=node_ids[0], t@@ -1048,7 +1063,7 @@ class Peer(PrintError): chan_ann = gen_msg("channel_announcement", len=0, - #features + #features not set (defaults to zeros) chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)), short_channel_id=chan.short_channel_id, node_id_1=node_ids[0], DIR diff --git a/lib/lnhtlc.py b/lib/lnhtlc.py t@@ -146,8 +146,6 @@ class HTLCStateMachine(PrintError): if htlc.r_locked_in is None: htlc.r_locked_in = self.state.remote_state.ctn assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes - assert len(htlc_sigs) == len(self.local_commitment.outputs()) - 2, (len(htlc_sigs), len(self.local_commitment.outputs()) - 2, self.diagnostic_name()) - preimage_hex = self.local_commitment.serialize_preimage(0) pre_hash = Hash(bfh(preimage_hex)) if not ecc.verify_signature(self.state.remote_config.multisig_key.pubkey, sig, pre_hash): t@@ -298,8 +296,8 @@ class HTLCStateMachine(PrintError): htlc_value_local remote_msat = self.state.remote_state.amount_msat -\ htlc_value_remote - assert local_msat > 0 - assert remote_msat > 0 + assert local_msat >= 0 + assert remote_msat >= 0 this_point = self.state.remote_state.next_per_commitment_point t@@ -320,7 +318,6 @@ class HTLCStateMachine(PrintError): commit = make_commitment_using_open_channel(self.state, self.state.remote_state.ctn + 1, False, this_point, remote_msat - total_fee_remote, local_msat - total_fee_local, htlcs_in_local + htlcs_in_remote) - assert len(commit.outputs()) == 2 + len(htlcs_in_local) + len(htlcs_in_remote) return commit @property t@@ -332,8 +329,8 @@ class HTLCStateMachine(PrintError): htlc_value_local remote_msat = self.state.remote_state.amount_msat -\ htlc_value_remote - assert local_msat > 0 - assert remote_msat > 0 + assert local_msat >= 0 + assert remote_msat >= 0 _, this_point, _ = self.points t@@ -354,7 +351,6 @@ class HTLCStateMachine(PrintError): commit = make_commitment_using_open_channel(self.state, self.state.local_state.ctn + 1, True, this_point, local_msat - total_fee_local, remote_msat - total_fee_remote, htlcs_in_local + htlcs_in_remote) - assert len(commit.outputs()) == 2 + len(htlcs_in_local) + len(htlcs_in_remote) return commit def gen_htlc_indices(self, subject): DIR diff --git a/lib/lnworker.py b/lib/lnworker.py t@@ -144,30 +144,29 @@ class LNWorker(PrintError): return None chan = chan._replace(short_channel_id = calc_short_channel_id(block_height, tx_pos, chan.funding_outpoint.output_index)) self.save_channel(chan) - return chan, conf - return None, None + return chan + return None def on_network_update(self, event, *args): for chan in self.channels.values(): - if self.channel_state[chan.channel_id] == "OPEN": - conf = self.wallet.get_tx_height(chan.funding_outpoint.txid)[1] - if conf >= 6: - peer = self.peers[chan.node_id] - coro = peer.funding_six_deep(chan) - fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop) - fut.result() - if self.channel_state[chan.channel_id] != "OPENING": - continue - chan, conf = self.save_short_chan_id(chan) - if not chan: - self.print_error("network update but funding tx is still not at sufficient depth") - continue peer = self.peers[chan.node_id] - peer.funding_locked(chan) + if self.channel_state[chan.channel_id] == "OPENING": + chan = self.save_short_chan_id(chan) + if not chan: + self.print_error("network update but funding tx is still not at sufficient depth") + continue + # this results in the channel being marked OPEN + peer.funding_locked(chan) + elif self.channel_state[chan.channel_id] == "OPEN": + conf = self.wallet.get_tx_height(chan.funding_outpoint.txid)[1] + peer.on_network_update(chan, conf) # not aiosafe because we call .result() which will propagate an exception async def _open_channel_coroutine(self, node_id, amount_sat, push_sat, password): - peer = self.peers[bfh(node_id)] + if node_id == "": + peer = next(iter(self.peers.values())) + else: + peer = self.peers[bfh(node_id)] openingchannel = await peer.channel_establishment_flow(self.wallet, self.config, password, amount_sat, push_sat * 1000, temp_channel_id=os.urandom(32)) self.print_error("SAVING OPENING CHANNEL") self.save_channel(openingchannel)