electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit b26dc66567fe40f5623d274a47b131893e368ee9
parent e8471e483b303e6b65f9a2979b9eb9ea530b0490
Author: Janus <ysangkok@gmail.com>
Date:   Thu, 11 Oct 2018 17:06:37 +0200

lnhtlc: only store feerate once, don't store heights since we do not roll back

Diffstat:
Melectrum/lnbase.py | 13+++++--------
Melectrum/lnhtlc.py | 59++++++++++++++++++++---------------------------------------
Melectrum/lnutil.py | 3+--
Melectrum/tests/test_lnhtlc.py | 31+++++++++++++++++--------------
4 files changed, 43 insertions(+), 63 deletions(-)

diff --git a/electrum/lnbase.py b/electrum/lnbase.py @@ -530,7 +530,7 @@ class Peer(PrintError): chan.set_state('DISCONNECTED') self.network.trigger_callback('channel', chan) - def make_local_config(self, funding_sat, push_msat, initiator: HTLCOwner, feerate): + def make_local_config(self, funding_sat, push_msat, initiator: HTLCOwner): # key derivation channel_counter = self.lnworker.get_and_inc_counter_for_channel_keys() keypair_generator = lambda family: generate_keypair(self.lnworker.ln_keystore, family, channel_counter) @@ -552,7 +552,6 @@ class Peer(PrintError): ctn=-1, next_htlc_id=0, amount_msat=initial_msat, - feerate=feerate, ) per_commitment_secret_seed = keypair_generator(LnKeyFamily.REVOCATION_ROOT).privkey return local_config, per_commitment_secret_seed @@ -561,7 +560,7 @@ class Peer(PrintError): async def channel_establishment_flow(self, password, funding_sat, push_msat, temp_channel_id): await self.initialized feerate = self.current_feerate_per_kw() - local_config, per_commitment_secret_seed = self.make_local_config(funding_sat, push_msat, LOCAL, feerate) + local_config, per_commitment_secret_seed = self.make_local_config(funding_sat, push_msat, LOCAL) # for the first commitment transaction per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, RevocationStore.START_INDEX) per_commitment_point_first = secret_to_pubkey(int.from_bytes(per_commitment_secret_first, 'big')) @@ -611,7 +610,6 @@ class Peer(PrintError): ctn = -1, amount_msat=push_msat, next_htlc_id = 0, - feerate=feerate, next_per_commitment_point=remote_per_commitment_point, current_per_commitment_point=None, @@ -640,7 +638,7 @@ class Peer(PrintError): current_commitment_signature = None, current_htlc_signatures = None, ), - "constraints": ChannelConstraints(capacity=funding_sat, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth), + "constraints": ChannelConstraints(capacity=funding_sat, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth, feerate=feerate), "remote_commitment_to_be_revoked": None, } m = HTLCStateMachine(chan) @@ -675,7 +673,7 @@ class Peer(PrintError): feerate = int.from_bytes(payload['feerate_per_kw'], 'big') temp_chan_id = payload['temporary_channel_id'] - local_config, per_commitment_secret_seed = self.make_local_config(funding_sat * 1000, push_msat, REMOTE, feerate) + local_config, per_commitment_secret_seed = self.make_local_config(funding_sat * 1000, push_msat, REMOTE) # for the first commitment transaction per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, RevocationStore.START_INDEX) @@ -723,7 +721,6 @@ class Peer(PrintError): ctn = -1, amount_msat=remote_balance_sat, next_htlc_id = 0, - feerate=feerate, next_per_commitment_point=payload['first_per_commitment_point'], current_per_commitment_point=None, @@ -737,7 +734,7 @@ class Peer(PrintError): current_commitment_signature = None, current_htlc_signatures = None, ), - "constraints": ChannelConstraints(capacity=funding_sat, is_initiator=False, funding_txn_minimum_depth=min_depth), + "constraints": ChannelConstraints(capacity=funding_sat, is_initiator=False, funding_txn_minimum_depth=min_depth, feerate=feerate), "remote_commitment_to_be_revoked": None, } m = HTLCStateMachine(chan) diff --git a/electrum/lnhtlc.py b/electrum/lnhtlc.py @@ -1,5 +1,5 @@ # ported from lnd 42de4400bff5105352d0552155f73589166d162b -from collections import namedtuple +from collections import namedtuple, defaultdict import binascii import json from enum import Enum, auto @@ -34,26 +34,14 @@ FUNDEE_SIGNED = FeeUpdateProgress.FUNDEE_SIGNED FUNDEE_ACKED = FeeUpdateProgress.FUNDEE_ACKED FUNDER_SIGNED = FeeUpdateProgress.FUNDER_SIGNED -from collections import namedtuple - -class FeeUpdate: +class FeeUpdate(defaultdict): def __init__(self, chan, rate): + super().__init__(lambda: False) self.rate = rate - self.progress = {FUNDEE_SIGNED: None, FUNDEE_ACKED: None, FUNDER_SIGNED: None} self.chan = chan - def set(self, field): - self.progress[field] = self.chan.current_height[LOCAL if self.chan.constraints.is_initiator else REMOTE] - - def had(self, field): - """ - returns true when the progress field given has been - set at the current commitment number of the funder - """ - return self.progress[field] is not None - def pending_feerate(self, subject): - if self.had(FUNDEE_ACKED): + if self[FUNDEE_ACKED]: return self.rate if subject == REMOTE and self.chan.constraints.is_initiator: return self.rate @@ -230,9 +218,9 @@ class HTLCStateMachine(PrintError): for pending_fee in self.fee_mgr: if not self.constraints.is_initiator: - pending_fee.set(FUNDEE_SIGNED) - if self.constraints.is_initiator and pending_fee.had(FUNDEE_ACKED): - pending_fee.set(FUNDER_SIGNED) + pending_fee[FUNDEE_SIGNED] = True + if self.constraints.is_initiator and pending_fee[FUNDEE_ACKED]: + pending_fee[FUNDER_SIGNED] = True self.process_new_offchain_ctx(pending_remote_commitment, ours=False) @@ -283,9 +271,9 @@ class HTLCStateMachine(PrintError): for pending_fee in self.fee_mgr: if not self.constraints.is_initiator: - pending_fee.set(FUNDEE_SIGNED) - if self.constraints.is_initiator and pending_fee.had(FUNDEE_ACKED): - pending_fee.set(FUNDER_SIGNED) + pending_fee[FUNDEE_SIGNED] = True + if self.constraints.is_initiator and pending_fee[FUNDEE_ACKED]: + pending_fee[FUNDER_SIGNED] = True self.process_new_offchain_ctx(pending_local_commitment, ours=True) @@ -305,25 +293,23 @@ class HTLCStateMachine(PrintError): last_secret, this_point, next_point = self.points - new_local_feerate = self.config[LOCAL].feerate - new_remote_feerate = self.config[REMOTE].feerate + new_feerate = self.constraints.feerate for pending_fee in self.fee_mgr[:]: - if not self.constraints.is_initiator and pending_fee.had(FUNDEE_SIGNED): - new_local_feerate = new_remote_feerate = pending_fee.rate + if not self.constraints.is_initiator and pending_fee[FUNDEE_SIGNED]: + new_feerate = pending_fee.rate self.fee_mgr.remove(pending_fee) print("FEERATE CHANGE COMPLETE (non-initiator)") - if self.constraints.is_initiator and pending_fee.had(FUNDER_SIGNED): - new_local_feerate = new_remote_feerate = pending_fee.rate + if self.constraints.is_initiator and pending_fee[FUNDER_SIGNED]: + new_feerate = pending_fee.rate self.fee_mgr.remove(pending_fee) print("FEERATE CHANGE COMPLETE (initiator)") self.config[LOCAL]=self.config[LOCAL]._replace( ctn=self.config[LOCAL].ctn + 1, - feerate=new_local_feerate ) - self.config[REMOTE]=self.config[REMOTE]._replace( - feerate=new_remote_feerate + self.constraints=self.constraints._replace( + feerate=new_feerate ) self.local_commitment = self.pending_local_commitment @@ -427,7 +413,7 @@ class HTLCStateMachine(PrintError): for pending_fee in self.fee_mgr: if self.constraints.is_initiator: - pending_fee.set(FUNDEE_ACKED) + pending_fee[FUNDEE_ACKED] = True self.local_commitment = self.pending_local_commitment self.remote_commitment = self.pending_remote_commitment @@ -471,18 +457,13 @@ class HTLCStateMachine(PrintError): return self.make_commitment(REMOTE, this_point) def pending_feerate(self, subject): - candidate = None + candidate = self.constraints.feerate for pending_fee in self.fee_mgr: x = pending_fee.pending_feerate(subject) if x is not None: candidate = x - feerate = candidate if candidate is not None else self._committed_feerate[subject] - return feerate - - @property - def _committed_feerate(self): - return {LOCAL: self.config[LOCAL].feerate, REMOTE: self.config[REMOTE].feerate} + return candidate @property def pending_local_commitment(self): diff --git a/electrum/lnutil.py b/electrum/lnutil.py @@ -26,7 +26,6 @@ common = [ ('ctn' , int), ('amount_msat' , int), ('next_htlc_id' , int), - ('feerate' , int), ('payment_basepoint' , Keypair), ('multisig_key' , Keypair), ('htlc_basepoint' , Keypair), @@ -49,7 +48,7 @@ LocalConfig = NamedTuple('LocalConfig', common + [ ('current_htlc_signatures', List[bytes]), ]) -ChannelConstraints = namedtuple("ChannelConstraints", ["capacity", "is_initiator", "funding_txn_minimum_depth"]) +ChannelConstraints = namedtuple("ChannelConstraints", ["capacity", "is_initiator", "funding_txn_minimum_depth", "feerate"]) ScriptHtlc = namedtuple('ScriptHtlc', ['redeem_script', 'htlc']) diff --git a/electrum/tests/test_lnhtlc.py b/electrum/tests/test_lnhtlc.py @@ -34,7 +34,6 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate initial_msat=remote_amount, ctn = 0, next_htlc_id = 0, - feerate=local_feerate, amount_msat=remote_amount, next_per_commitment_point=nex, @@ -54,7 +53,6 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate initial_msat=local_amount, ctn = 0, next_htlc_id = 0, - feerate=local_feerate, amount_msat=local_amount, per_commitment_secret_seed=seed, @@ -63,7 +61,12 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate current_commitment_signature=None, current_htlc_signatures=None, ), - "constraints":lnbase.ChannelConstraints(capacity=funding_sat, is_initiator=is_initiator, funding_txn_minimum_depth=3), + "constraints":lnbase.ChannelConstraints( + capacity=funding_sat, + is_initiator=is_initiator, + funding_txn_minimum_depth=3, + feerate=local_feerate, + ), "node_id":other_node_id, "remote_commitment_to_be_revoked": None, 'onion_keys': {}, @@ -271,20 +274,20 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase): bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs) - self.assertNotEqual(fee, bob_channel.config[LOCAL].feerate) + self.assertNotEqual(fee, bob_channel.constraints.feerate) rev, _ = bob_channel.revoke_current_commitment() - self.assertEqual(fee, bob_channel.config[LOCAL].feerate) + self.assertEqual(fee, bob_channel.constraints.feerate) bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment() alice_channel.receive_revocation(rev) alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs) - self.assertNotEqual(fee, alice_channel.config[LOCAL].feerate) + self.assertNotEqual(fee, alice_channel.constraints.feerate) rev, _ = alice_channel.revoke_current_commitment() - self.assertEqual(fee, alice_channel.config[LOCAL].feerate) + self.assertEqual(fee, alice_channel.constraints.feerate) bob_channel.receive_revocation(rev) - self.assertEqual(fee, bob_channel.config[REMOTE].feerate) + self.assertEqual(fee, bob_channel.constraints.feerate) def test_UpdateFeeReceiverCommits(self): @@ -300,20 +303,20 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase): alice_sig, alice_htlc_sigs = alice_channel.sign_next_commitment() bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs) - self.assertNotEqual(fee, bob_channel.config[LOCAL].feerate) + self.assertNotEqual(fee, bob_channel.constraints.feerate) bob_revocation, _ = bob_channel.revoke_current_commitment() - self.assertEqual(fee, bob_channel.config[LOCAL].feerate) + self.assertEqual(fee, bob_channel.constraints.feerate) bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment() alice_channel.receive_revocation(bob_revocation) alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs) - self.assertNotEqual(fee, alice_channel.config[LOCAL].feerate) + self.assertNotEqual(fee, alice_channel.constraints.feerate) alice_revocation, _ = alice_channel.revoke_current_commitment() - self.assertEqual(fee, alice_channel.config[LOCAL].feerate) + self.assertEqual(fee, alice_channel.constraints.feerate) bob_channel.receive_revocation(alice_revocation) - self.assertEqual(fee, bob_channel.config[REMOTE].feerate) + self.assertEqual(fee, bob_channel.constraints.feerate) @@ -323,7 +326,7 @@ class TestLNHTLCDust(unittest.TestCase): paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) - fee_per_kw = alice_channel.config[LOCAL].feerate + fee_per_kw = alice_channel.constraints.feerate self.assertEqual(fee_per_kw, 6000) htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000) self.assertEqual(htlcAmt, 4478)