commit b1f606eaed10e6d6d3ead481301879ff065ee14a
parent e32807d29d0484b5bb834affc5fd163274991e13
Author: SomberNight <somber.night@protonmail.com>
Date: Sat, 3 Aug 2019 17:34:11 +0200
lnchannel: start using "latest" and "next" instead of "current" and "pending"
"current" used to be "oldest_unrevoked"; and pending was "oldest_unrevoked + 1"
but this was very confusing...
so now we have "oldest_unrevoked", "latest", and "next"
where "next" is "latest + 1"
"oldest_unrevoked" and "latest" are either the same or are offset by 1
(but caller should know which one they need)
rm "got_sig_for_next" - it was a redundant sanity check, that really
just complicated things
rm "local_commitment", "remote_commitment", "set_local_commitment",
"set_remote_commitment" - just use "get_latest_commitment" instead
Diffstat:
6 files changed, 126 insertions(+), 154 deletions(-)
diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
@@ -146,8 +146,6 @@ class Channel(Logger):
self._is_funding_txo_spent = None # "don't know"
self._state = None
self.set_state('DISCONNECTED')
- self.local_commitment = None
- self.remote_commitment = None
self.sweep_info = {}
def get_feerate(self, subject, ctn):
@@ -175,14 +173,6 @@ class Channel(Logger):
out[rhash] = (self.channel_id, htlc, direction, status)
return out
- def set_local_commitment(self, ctx):
- ctn = extract_ctn_from_tx_and_chan(ctx, self)
- assert self.signature_fits(ctx), (self.hm.log[LOCAL])
- self.local_commitment = ctx
-
- def set_remote_commitment(self):
- self.remote_commitment = self.current_commitment(REMOTE)
-
def open_with_first_pcp(self, remote_pcp, remote_sig):
self.config[REMOTE] = self.config[REMOTE]._replace(ctn=0, current_per_commitment_point=remote_pcp, next_per_commitment_point=None)
self.config[LOCAL] = self.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig)
@@ -285,10 +275,10 @@ class Channel(Logger):
This docstring was adapted from LND.
"""
- next_remote_ctn = self.get_current_ctn(REMOTE) + 1
+ next_remote_ctn = self.get_next_ctn(REMOTE)
self.logger.info(f"sign_next_commitment {next_remote_ctn}")
- self.hm.send_ctx()
- pending_remote_commitment = self.pending_commitment(REMOTE)
+
+ pending_remote_commitment = self.get_next_commitment(REMOTE)
sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.config[LOCAL], self.config[REMOTE])
their_remote_htlc_privkey_number = derive_privkey(
@@ -317,8 +307,7 @@ class Channel(Logger):
htlcsigs.sort()
htlcsigs = [x[1] for x in htlcsigs]
- # TODO should add remote_commitment here and handle
- # both valid ctx'es in lnwatcher at the same time...
+ self.hm.send_ctx()
return sig_64, htlcsigs
@@ -335,22 +324,20 @@ class Channel(Logger):
This docstring is from LND.
"""
+ next_local_ctn = self.get_next_ctn(LOCAL)
self.logger.info("receive_new_commitment")
- self.hm.recv_ctx()
-
assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes
- pending_local_commitment = self.pending_commitment(LOCAL)
+ pending_local_commitment = self.get_next_commitment(LOCAL)
preimage_hex = pending_local_commitment.serialize_preimage(0)
pre_hash = sha256d(bfh(preimage_hex))
if not ecc.verify_signature(self.config[REMOTE].multisig_key.pubkey, sig, pre_hash):
- raise Exception('failed verifying signature of our updated commitment transaction: ' + bh2u(sig) + ' preimage is ' + preimage_hex)
+ raise Exception(f'failed verifying signature of our updated commitment transaction: {bh2u(sig)} preimage is {preimage_hex}')
htlc_sigs_string = b''.join(htlc_sigs)
htlc_sigs = htlc_sigs[:] # copy cause we will delete now
- next_local_ctn = self.get_current_ctn(LOCAL) + 1
for htlcs, we_receive in [(self.included_htlcs(LOCAL, SENT, ctn=next_local_ctn), False),
(self.included_htlcs(LOCAL, RECEIVED, ctn=next_local_ctn), True)]:
for htlc in htlcs:
@@ -359,12 +346,10 @@ class Channel(Logger):
if len(htlc_sigs) != 0: # all sigs should have been popped above
raise Exception('failed verifying HTLC signatures: invalid amount of correct signatures')
+ self.hm.recv_ctx()
self.config[LOCAL]=self.config[LOCAL]._replace(
current_commitment_signature=sig,
- current_htlc_signatures=htlc_sigs_string,
- got_sig_for_next=True)
-
- self.set_local_commitment(pending_local_commitment)
+ current_htlc_signatures=htlc_sigs_string)
def verify_htlc(self, htlc: UpdateAddHtlc, htlc_sigs: Sequence[bytes], we_receive: bool, ctx) -> int:
ctn = extract_ctn_from_tx_and_chan(ctx, self)
@@ -394,15 +379,14 @@ class Channel(Logger):
def revoke_current_commitment(self):
self.logger.info("revoke_current_commitment")
- assert self.config[LOCAL].got_sig_for_next
new_ctn = self.config[LOCAL].ctn + 1
- new_ctx = self.pending_commitment(LOCAL)
- assert self.signature_fits(new_ctx)
- self.set_local_commitment(new_ctx)
+ new_ctx = self.get_latest_commitment(LOCAL)
+ if not self.signature_fits(new_ctx):
+ # this should never fail; as receive_new_commitment already did this test
+ raise Exception("refusing to revoke as remote sig does not fit")
self.hm.send_rev()
self.config[LOCAL]=self.config[LOCAL]._replace(
ctn=new_ctn,
- got_sig_for_next=False,
)
received = self.hm.received_in_ctn(new_ctn)
sent = self.hm.sent_in_ctn(new_ctn)
@@ -427,14 +411,12 @@ class Channel(Logger):
self.config[REMOTE].revocation_store.add_next_entry(revocation.per_commitment_secret)
##### start applying fee/htlc changes
- next_point = self.config[REMOTE].next_per_commitment_point
self.hm.recv_rev()
self.config[REMOTE]=self.config[REMOTE]._replace(
ctn=self.config[REMOTE].ctn + 1,
- current_per_commitment_point=next_point,
+ current_per_commitment_point=self.config[REMOTE].next_per_commitment_point,
next_per_commitment_point=revocation.next_per_commitment_point,
)
- self.set_remote_commitment()
def balance(self, whose, *, ctx_owner=HTLCOwner.LOCAL, ctn=None):
"""
@@ -512,7 +494,7 @@ class Channel(Logger):
def get_secret_and_point(self, subject, ctn) -> Tuple[Optional[bytes], bytes]:
assert type(subject) is HTLCOwner
- offset = ctn - self.get_current_ctn(subject)
+ offset = ctn - self.get_oldest_unrevoked_ctn(subject)
if subject == REMOTE:
if offset > 1:
raise RemoteCtnTooFarInFuture(f"offset: {offset}")
@@ -540,12 +522,16 @@ class Channel(Logger):
secret, ctx = self.get_secret_and_commitment(subject, ctn)
return ctx
- def pending_commitment(self, subject):
- ctn = self.get_current_ctn(subject)
- return self.get_commitment(subject, ctn + 1)
+ def get_next_commitment(self, subject: HTLCOwner) -> Transaction:
+ ctn = self.get_next_ctn(subject)
+ return self.get_commitment(subject, ctn)
+
+ def get_latest_commitment(self, subject: HTLCOwner) -> Transaction:
+ ctn = self.get_latest_ctn(subject)
+ return self.get_commitment(subject, ctn)
- def current_commitment(self, subject):
- ctn = self.get_current_ctn(subject)
+ def get_oldest_unrevoked_commitment(self, subject: HTLCOwner) -> Transaction:
+ ctn = self.get_oldest_unrevoked_ctn(subject)
return self.get_commitment(subject, ctn)
def create_sweeptxs(self, ctn):
@@ -553,9 +539,15 @@ class Channel(Logger):
secret, ctx = self.get_secret_and_commitment(REMOTE, ctn)
return create_sweeptxs_for_watchtower(self, ctx, secret, self.sweep_address)
- def get_current_ctn(self, subject):
+ def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int:
return self.config[subject].ctn
+ def get_latest_ctn(self, subject: HTLCOwner) -> int:
+ return self.hm.ctn_latest(subject)
+
+ def get_next_ctn(self, subject: HTLCOwner) -> int:
+ return self.hm.ctn_latest(subject) + 1
+
def total_msat(self, direction):
"""Return the cumulative total msat amount received/sent so far."""
assert type(direction) is Direction
@@ -597,12 +589,8 @@ class Channel(Logger):
self.logger.info("receive_fail_htlc")
self.hm.recv_fail(htlc_id)
- @property
- def current_height(self):
- return {LOCAL: self.config[LOCAL].ctn, REMOTE: self.config[REMOTE].ctn}
-
def pending_local_fee(self):
- return self.constraints.capacity - sum(x[2] for x in self.pending_commitment(LOCAL).outputs())
+ return self.constraints.capacity - sum(x[2] for x in self.get_next_commitment(LOCAL).outputs())
def update_fee(self, feerate: int, from_us: bool):
# feerate uses sat/kw
@@ -751,7 +739,7 @@ class Channel(Logger):
return res
def force_close_tx(self):
- tx = self.local_commitment
+ tx = self.get_latest_commitment(LOCAL)
assert self.signature_fits(tx)
tx = Transaction(str(tx))
tx.deserialize(True)
diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
@@ -458,7 +458,6 @@ class Peer(Logger):
was_announced=False,
current_commitment_signature=None,
current_htlc_signatures=[],
- got_sig_for_next=False,
)
return local_config
@@ -577,8 +576,6 @@ class Peer(Logger):
# broadcast funding tx
await asyncio.wait_for(self.network.broadcast_transaction(funding_tx), 5)
chan.open_with_first_pcp(remote_per_commitment_point, remote_sig)
- chan.set_remote_commitment()
- chan.set_local_commitment(chan.current_commitment(LOCAL))
return chan
async def on_open_channel(self, payload):
@@ -713,12 +710,12 @@ class Peer(Logger):
# BOLT-02: "A node [...] upon disconnection [...] MUST reverse any uncommitted updates sent by the other side"
chan.hm.discard_unsigned_remote_updates()
# ctns
- oldest_unrevoked_local_ctn = chan.config[LOCAL].ctn
- latest_local_ctn = chan.hm.ctn_latest(LOCAL)
- next_local_ctn = latest_local_ctn + 1
- oldest_unrevoked_remote_ctn = chan.config[REMOTE].ctn
- latest_remote_ctn = chan.hm.ctn_latest(REMOTE)
- next_remote_ctn = latest_remote_ctn + 1
+ oldest_unrevoked_local_ctn = chan.get_oldest_unrevoked_ctn(LOCAL)
+ latest_local_ctn = chan.get_latest_ctn(LOCAL)
+ next_local_ctn = chan.get_next_ctn(LOCAL)
+ oldest_unrevoked_remote_ctn = chan.get_oldest_unrevoked_ctn(REMOTE)
+ latest_remote_ctn = chan.get_latest_ctn(REMOTE)
+ next_remote_ctn = chan.get_next_ctn(REMOTE)
# send message
dlp_enabled = self.localfeatures & LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
if dlp_enabled:
@@ -1016,7 +1013,7 @@ class Peer(Logger):
htlc_id = int.from_bytes(payload["id"], "big")
chan = self.channels[channel_id]
chan.receive_fail_htlc(htlc_id)
- local_ctn = chan.get_current_ctn(LOCAL)
+ local_ctn = chan.get_latest_ctn(LOCAL)
asyncio.ensure_future(self._handle_error_code_from_failed_htlc(payload, channel_id, htlc_id))
asyncio.ensure_future(self._on_update_fail_htlc(channel_id, htlc_id, local_ctn))
@@ -1087,7 +1084,7 @@ class Peer(Logger):
self.network.path_finder.add_to_blacklist(short_chan_id)
def maybe_send_commitment(self, chan: Channel):
- ctn_to_sign = chan.get_current_ctn(REMOTE) + 1
+ ctn_to_sign = chan.get_next_ctn(REMOTE)
# if there are no changes, we will not (and must not) send a new commitment
next_htlcs, latest_htlcs = chan.hm.get_htlcs_in_next_ctx(REMOTE), chan.hm.get_htlcs_in_latest_ctx(REMOTE)
if (next_htlcs == latest_htlcs
@@ -1101,12 +1098,12 @@ class Peer(Logger):
async def await_remote(self, chan: Channel, ctn: int):
self.maybe_send_commitment(chan)
- while chan.get_current_ctn(REMOTE) <= ctn:
+ while chan.get_latest_ctn(REMOTE) <= ctn:
await self._remote_changed_events[chan.channel_id].wait()
async def await_local(self, chan: Channel, ctn: int):
self.maybe_send_commitment(chan)
- while chan.get_current_ctn(LOCAL) <= ctn:
+ while chan.get_latest_ctn(LOCAL) <= ctn:
await self._local_changed_events[chan.channel_id].wait()
async def pay(self, route: List['RouteEdge'], chan: Channel, amount_msat: int,
@@ -1122,7 +1119,7 @@ class Peer(Logger):
# create htlc
htlc = UpdateAddHtlc(amount_msat=amount_msat, payment_hash=payment_hash, cltv_expiry=cltv, timestamp=int(time.time()))
htlc = chan.add_htlc(htlc)
- remote_ctn = chan.get_current_ctn(REMOTE)
+ remote_ctn = chan.get_latest_ctn(REMOTE)
chan.onion_keys[htlc.htlc_id] = secret_key
self.attempted_route[(chan.channel_id, htlc.htlc_id)] = route
self.logger.info(f"starting payment. route: {route}. htlc: {htlc}")
@@ -1156,7 +1153,7 @@ class Peer(Logger):
and chan.get_next_feerate(LOCAL) == chan.get_latest_feerate(LOCAL)):
raise RemoteMisbehaving('received commitment_signed without pending changes')
# make sure ctn is new
- ctn_to_recv = chan.get_current_ctn(LOCAL) + 1
+ ctn_to_recv = chan.get_next_ctn(LOCAL)
if ctn_to_recv == self.recv_commitment_for_ctn_last[chan]:
raise RemoteMisbehaving('received commitment_signed with same ctn')
self.recv_commitment_for_ctn_last[chan] = ctn_to_recv
@@ -1172,7 +1169,7 @@ class Peer(Logger):
preimage = update_fulfill_htlc_msg["payment_preimage"]
htlc_id = int.from_bytes(update_fulfill_htlc_msg["id"], "big")
chan.receive_htlc_settle(preimage, htlc_id)
- local_ctn = chan.get_current_ctn(LOCAL)
+ local_ctn = chan.get_latest_ctn(LOCAL)
asyncio.ensure_future(self._on_update_fulfill_htlc(chan, htlc_id, preimage, local_ctn))
@log_exceptions
@@ -1206,8 +1203,8 @@ class Peer(Logger):
timestamp=int(time.time()),
htlc_id=htlc_id)
htlc = chan.receive_htlc(htlc)
- local_ctn = chan.get_current_ctn(LOCAL)
- remote_ctn = chan.get_current_ctn(REMOTE)
+ local_ctn = chan.get_latest_ctn(LOCAL)
+ remote_ctn = chan.get_latest_ctn(REMOTE)
if processed_onion.are_we_final:
asyncio.ensure_future(self._maybe_fulfill_htlc(chan=chan,
htlc=htlc,
@@ -1243,7 +1240,7 @@ class Peer(Logger):
next_amount_msat_htlc = int.from_bytes(dph.amt_to_forward, 'big')
next_htlc = UpdateAddHtlc(amount_msat=next_amount_msat_htlc, payment_hash=htlc.payment_hash, cltv_expiry=next_cltv_expiry, timestamp=int(time.time()))
next_htlc = next_chan.add_htlc(next_htlc)
- next_remote_ctn = next_chan.get_current_ctn(REMOTE)
+ next_remote_ctn = next_chan.get_latest_ctn(REMOTE)
next_peer.send_message(
"update_add_htlc",
channel_id=next_chan.channel_id,
@@ -1301,7 +1298,7 @@ class Peer(Logger):
async def _fulfill_htlc(self, chan: Channel, htlc_id: int, preimage: bytes):
chan.settle_htlc(preimage, htlc_id)
- remote_ctn = chan.get_current_ctn(REMOTE)
+ remote_ctn = chan.get_latest_ctn(REMOTE)
self.send_message("update_fulfill_htlc",
channel_id=chan.channel_id,
id=htlc_id,
@@ -1313,7 +1310,7 @@ class Peer(Logger):
reason: OnionRoutingFailureMessage):
self.logger.info(f"failing received htlc {(bh2u(chan.channel_id), htlc_id)}. reason: {reason}")
chan.fail_htlc(htlc_id)
- remote_ctn = chan.get_current_ctn(REMOTE)
+ remote_ctn = chan.get_latest_ctn(REMOTE)
error_packet = construct_onion_error(reason, onion_packet, our_onion_private_key=self.privkey)
self.send_message("update_fail_htlc",
channel_id=chan.channel_id,
@@ -1357,7 +1354,7 @@ class Peer(Logger):
else:
return
chan.update_fee(feerate_per_kw, True)
- remote_ctn = chan.get_current_ctn(REMOTE)
+ remote_ctn = chan.get_latest_ctn(REMOTE)
self.send_message("update_fee",
channel_id=chan.channel_id,
feerate_per_kw=feerate_per_kw)
diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py
@@ -188,7 +188,7 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
# other outputs are htlcs
# if they are spent, we need to generate the script
# so, second-stage htlc sweep should not be returned here
- if ctn != our_conf.ctn:
+ if ctn < chan.get_oldest_unrevoked_ctn(LOCAL):
_logger.info("we breached.")
return {}
txs = {}
@@ -247,17 +247,18 @@ def create_sweeptxs_for_our_ctx(chan: 'Channel', ctx: Transaction, ctn: int,
def analyze_ctx(chan: 'Channel', ctx: Transaction):
# note: the remote sometimes has two valid non-revoked commitment transactions,
- # either of which could be broadcast (their_conf.ctn, their_conf.ctn+1)
+ # either of which could be broadcast
our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True)
ctn = extract_ctn_from_tx_and_chan(ctx, chan)
per_commitment_secret = None
- if ctn == their_conf.ctn:
+ oldest_unrevoked_remote_ctn = chan.get_oldest_unrevoked_ctn(REMOTE)
+ if ctn == oldest_unrevoked_remote_ctn:
their_pcp = their_conf.current_per_commitment_point
is_revocation = False
- elif ctn == their_conf.ctn + 1:
+ elif ctn == oldest_unrevoked_remote_ctn + 1:
their_pcp = their_conf.next_per_commitment_point
is_revocation = False
- elif ctn < their_conf.ctn: # breach
+ elif ctn < oldest_unrevoked_remote_ctn: # breach
try:
per_commitment_secret = their_conf.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn)
except UnableToDeriveSecret:
diff --git a/electrum/lnutil.py b/electrum/lnutil.py
@@ -52,7 +52,6 @@ class LocalConfig(NamedTuple):
was_announced: bool
current_commitment_signature: Optional[bytes]
current_htlc_signatures: List[bytes]
- got_sig_for_next: bool
class RemoteConfig(NamedTuple):
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
@@ -311,8 +311,6 @@ class LNWallet(LNWorker):
for x in wallet.storage.get("channels", []):
c = Channel(x, sweep_address=self.sweep_address, lnworker=self)
self.channels[c.channel_id] = c
- c.set_remote_commitment()
- c.set_local_commitment(c.current_commitment(LOCAL))
# timestamps of opening and closing transactions
self.channel_timestamps = self.storage.get('lightning_channel_timestamps', {})
self.pending_payments = defaultdict(asyncio.Future)
@@ -348,10 +346,10 @@ class LNWallet(LNWorker):
self.logger.info(f'could not contact remote watchtower {watchtower_url}')
await asyncio.sleep(5)
- async def sync_channel_with_watchtower(self, chan, watchtower):
+ async def sync_channel_with_watchtower(self, chan: Channel, watchtower):
outpoint = chan.funding_outpoint.to_str()
addr = chan.get_funding_address()
- current_ctn = chan.get_current_ctn(REMOTE)
+ current_ctn = chan.get_oldest_unrevoked_ctn(REMOTE)
watchtower_ctn = await watchtower.get_ctn(outpoint, addr)
for ctn in range(watchtower_ctn + 1, current_ctn):
sweeptxs = chan.create_sweeptxs(ctn)
diff --git a/electrum/tests/test_lnchannel.py b/electrum/tests/test_lnchannel.py
@@ -85,7 +85,6 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
was_announced=False,
current_commitment_signature=None,
current_htlc_signatures=None,
- got_sig_for_next=False,
),
"constraints":lnpeer.ChannelConstraints(
capacity=funding_sat,
@@ -93,7 +92,6 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
funding_txn_minimum_depth=3,
),
"node_id":other_node_id,
- "remote_commitment_to_be_revoked": None,
'onion_keys': {},
}
@@ -137,8 +135,8 @@ def create_test_channels(feerate=6000, local=None, remote=None):
alice.set_state('OPEN')
bob.set_state('OPEN')
- a_out = alice.current_commitment(LOCAL).outputs()
- b_out = bob.pending_commitment(REMOTE).outputs()
+ a_out = alice.get_latest_commitment(LOCAL).outputs()
+ b_out = bob.get_next_commitment(REMOTE).outputs()
assert a_out == b_out, "\n" + pformat((a_out, b_out))
sig_from_bob, a_htlc_sigs = bob.sign_next_commitment()
@@ -150,21 +148,12 @@ def create_test_channels(feerate=6000, local=None, remote=None):
alice.config[LOCAL] = alice.config[LOCAL]._replace(current_commitment_signature=sig_from_bob)
bob.config[LOCAL] = bob.config[LOCAL]._replace(current_commitment_signature=sig_from_alice)
- alice.set_local_commitment(alice.current_commitment(LOCAL))
- bob.set_local_commitment(bob.current_commitment(LOCAL))
-
alice_second = lnutil.secret_to_pubkey(int.from_bytes(lnutil.get_per_commitment_secret_from_seed(alice_seed, lnutil.RevocationStore.START_INDEX - 1), "big"))
bob_second = lnutil.secret_to_pubkey(int.from_bytes(lnutil.get_per_commitment_secret_from_seed(bob_seed, lnutil.RevocationStore.START_INDEX - 1), "big"))
alice.config[REMOTE] = alice.config[REMOTE]._replace(next_per_commitment_point=bob_second, current_per_commitment_point=bob_first)
bob.config[REMOTE] = bob.config[REMOTE]._replace(next_per_commitment_point=alice_second, current_per_commitment_point=alice_first)
- alice.set_remote_commitment()
- bob.set_remote_commitment()
-
- alice.remote_commitment_to_be_revoked = alice.remote_commitment
- bob.remote_commitment_to_be_revoked = bob.remote_commitment
-
alice.config[REMOTE] = alice.config[REMOTE]._replace(ctn=0)
bob.config[REMOTE] = bob.config[REMOTE]._replace(ctn=0)
alice.hm.channel_open_finished()
@@ -179,7 +168,7 @@ class TestFee(unittest.TestCase):
"""
def test_fee(self):
alice_channel, bob_channel = create_test_channels(253, 10000000000, 5000000000)
- self.assertIn(9999817, [x[2] for x in alice_channel.local_commitment.outputs()])
+ self.assertIn(9999817, [x[2] for x in alice_channel.get_latest_commitment(LOCAL).outputs()])
class TestChannel(unittest.TestCase):
maxDiff = 999
@@ -228,31 +217,43 @@ class TestChannel(unittest.TestCase):
self.htlc_dict['amount_msat'] += 1000
self.bob_channel.add_htlc(self.htlc_dict)
self.alice_channel.receive_htlc(self.htlc_dict)
+
+ self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
+ self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
+ self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
+ self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3)
+
self.alice_channel.receive_new_commitment(*self.bob_channel.sign_next_commitment())
- self.assertEqual(len(self.alice_channel.pending_commitment(REMOTE).outputs()), 3)
+
+ self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3)
+ self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
+ self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
+ self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3)
+
self.alice_channel.revoke_current_commitment()
- self.assertEqual(len(self.alice_channel.pending_commitment(REMOTE).outputs()), 4)
+
+ self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3)
+ self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
+ self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
+ self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 4)
def test_SimpleAddSettleWorkflow(self):
alice_channel, bob_channel = self.alice_channel, self.bob_channel
htlc = self.htlc
- alice_out = alice_channel.current_commitment(LOCAL).outputs()
+ alice_out = alice_channel.get_latest_commitment(LOCAL).outputs()
short_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 42]
long_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 62]
self.assertLess(alice_out[long_idx].value, 5 * 10**8, alice_out)
self.assertEqual(alice_out[short_idx].value, 5 * 10**8, alice_out)
- alice_out = alice_channel.current_commitment(REMOTE).outputs()
+ alice_out = alice_channel.get_latest_commitment(REMOTE).outputs()
short_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 42]
long_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 62]
self.assertLess(alice_out[short_idx].value, 5 * 10**8)
self.assertEqual(alice_out[long_idx].value, 5 * 10**8)
- def com():
- return alice_channel.local_commitment
-
- self.assertTrue(alice_channel.signature_fits(com()))
+ self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
self.assertNotEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [])
@@ -270,9 +271,9 @@ class TestChannel(unittest.TestCase):
from electrum.lnutil import extract_ctn_from_tx_and_chan
tx0 = str(alice_channel.force_close_tx())
- self.assertEqual(alice_channel.config[LOCAL].ctn, 0)
+ self.assertEqual(alice_channel.get_oldest_unrevoked_ctn(LOCAL), 0)
self.assertEqual(extract_ctn_from_tx_and_chan(alice_channel.force_close_tx(), alice_channel), 0)
- self.assertTrue(alice_channel.signature_fits(alice_channel.current_commitment(LOCAL)))
+ self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
# Next alice commits this change by sending a signature message. Since
# we expect the messages to be ordered, Bob will receive the HTLC we
@@ -281,21 +282,20 @@ class TestChannel(unittest.TestCase):
aliceSig, aliceHtlcSigs = alice_channel.sign_next_commitment()
self.assertEqual(len(aliceHtlcSigs), 1, "alice should generate one htlc signature")
- self.assertTrue(alice_channel.signature_fits(com()))
- self.assertEqual(str(alice_channel.current_commitment(LOCAL)), str(com()))
+ self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
self.assertEqual(next(iter(alice_channel.hm.get_htlcs_in_next_ctx(REMOTE)))[0], RECEIVED)
self.assertEqual(alice_channel.hm.get_htlcs_in_next_ctx(REMOTE), bob_channel.hm.get_htlcs_in_next_ctx(LOCAL))
- self.assertEqual(alice_channel.pending_commitment(REMOTE).outputs(), bob_channel.pending_commitment(LOCAL).outputs())
+ self.assertEqual(alice_channel.get_latest_commitment(REMOTE).outputs(), bob_channel.get_next_commitment(LOCAL).outputs())
# Bob receives this signature message, and checks that this covers the
# state he has in his remote log. This includes the HTLC just sent
# from Alice.
- self.assertTrue(bob_channel.signature_fits(bob_channel.current_commitment(LOCAL)))
+ self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
bob_channel.receive_new_commitment(aliceSig, aliceHtlcSigs)
- self.assertTrue(bob_channel.signature_fits(bob_channel.pending_commitment(LOCAL)))
+ self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
- self.assertEqual(bob_channel.config[REMOTE].ctn, 0)
+ self.assertEqual(bob_channel.get_oldest_unrevoked_ctn(REMOTE), 0)
self.assertEqual(bob_channel.included_htlcs(LOCAL, RECEIVED, 1), [htlc])#
self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 0), [])
@@ -311,57 +311,50 @@ class TestChannel(unittest.TestCase):
# has a valid signature for a newer commitment.
bobRevocation, _ = bob_channel.revoke_current_commitment()
bob_channel.serialize()
- self.assertTrue(bob_channel.signature_fits(bob_channel.current_commitment(LOCAL)))
+ self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
# Bob finally sends a signature for Alice's commitment transaction.
# This signature will cover the HTLC, since Bob will first send the
# revocation just created. The revocation also acks every received
# HTLC up to the point where Alice sent her signature.
bobSig, bobHtlcSigs = bob_channel.sign_next_commitment()
- self.assertTrue(bob_channel.signature_fits(bob_channel.current_commitment(LOCAL)))
+ self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
self.assertEqual(len(bobHtlcSigs), 1)
- self.assertTrue(alice_channel.signature_fits(com()))
- self.assertEqual(str(alice_channel.current_commitment(LOCAL)), str(com()))
+ self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
# so far: Alice added htlc, Alice signed.
- self.assertEqual(len(alice_channel.current_commitment(LOCAL).outputs()), 2)
- self.assertEqual(len(alice_channel.pending_commitment(LOCAL).outputs()), 2)
- self.assertEqual(len(alice_channel.current_commitment(REMOTE).outputs()), 2) # oldest unrevoked
- self.assertEqual(len(alice_channel.pending_commitment(REMOTE).outputs()), 3) # latest
+ self.assertEqual(len(alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
+ self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2)
+ self.assertEqual(len(alice_channel.get_oldest_unrevoked_commitment(REMOTE).outputs()), 2)
+ self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3)
# Alice then processes this revocation, sending her own revocation for
# her prior commitment transaction. Alice shouldn't have any HTLCs to
# forward since she's sending an outgoing HTLC.
alice_channel.receive_revocation(bobRevocation)
alice_channel.serialize()
- self.assertEqual(alice_channel.remote_commitment.outputs(), alice_channel.current_commitment(REMOTE).outputs())
- self.assertTrue(alice_channel.signature_fits(com()))
- self.assertTrue(alice_channel.signature_fits(alice_channel.current_commitment(LOCAL)))
+ self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
alice_channel.serialize()
- self.assertEqual(str(alice_channel.current_commitment(LOCAL)), str(com()))
- self.assertEqual(len(alice_channel.current_commitment(LOCAL).outputs()), 2)
- self.assertEqual(len(alice_channel.current_commitment(REMOTE).outputs()), 3)
- self.assertEqual(len(com().outputs()), 2)
+ self.assertEqual(len(alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
+ self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3)
self.assertEqual(len(alice_channel.force_close_tx().outputs()), 2)
self.assertEqual(len(alice_channel.hm.log[LOCAL]['adds']), 1)
alice_channel.serialize()
- self.assertEqual(alice_channel.pending_commitment(LOCAL).outputs(),
- bob_channel.pending_commitment(REMOTE).outputs())
+ self.assertEqual(alice_channel.get_next_commitment(LOCAL).outputs(),
+ bob_channel.get_latest_commitment(REMOTE).outputs())
# Alice then processes bob's signature, and since she just received
# the revocation, she expect this signature to cover everything up to
# the point where she sent her signature, including the HTLC.
alice_channel.receive_new_commitment(bobSig, bobHtlcSigs)
- self.assertEqual(alice_channel.remote_commitment.outputs(), alice_channel.current_commitment(REMOTE).outputs())
- self.assertEqual(len(alice_channel.current_commitment(REMOTE).outputs()), 3)
- self.assertEqual(len(com().outputs()), 3)
+ self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3)
self.assertEqual(len(alice_channel.force_close_tx().outputs()), 3)
self.assertEqual(len(alice_channel.hm.log[LOCAL]['adds']), 1)
@@ -371,10 +364,8 @@ class TestChannel(unittest.TestCase):
self.assertNotEqual(tx0, tx1)
# Alice then generates a revocation for bob.
- self.assertEqual(alice_channel.remote_commitment.outputs(), alice_channel.current_commitment(REMOTE).outputs())
aliceRevocation, _ = alice_channel.revoke_current_commitment()
alice_channel.serialize()
- #self.assertEqual(alice_channel.remote_commitment.outputs(), alice_channel.current_commitment(REMOTE).outputs())
tx2 = str(alice_channel.force_close_tx())
# since alice already has the signature for the next one, it doesn't change her force close tx (it was already the newer one)
@@ -384,7 +375,7 @@ class TestChannel(unittest.TestCase):
# is fully locked in within both commitment transactions. Bob should
# also be able to forward an HTLC now that the HTLC has been locked
# into both commitment transactions.
- self.assertTrue(bob_channel.signature_fits(bob_channel.current_commitment(LOCAL)))
+ self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
bob_channel.receive_revocation(aliceRevocation)
bob_channel.serialize()
@@ -398,13 +389,13 @@ class TestChannel(unittest.TestCase):
self.assertEqual(alice_channel.total_msat(RECEIVED), bobSent, "alice has incorrect milli-satoshis received")
self.assertEqual(bob_channel.total_msat(SENT), bobSent, "bob has incorrect milli-satoshis sent")
self.assertEqual(bob_channel.total_msat(RECEIVED), aliceSent, "bob has incorrect milli-satoshis received")
- self.assertEqual(bob_channel.config[LOCAL].ctn, 1, "bob has incorrect commitment height")
- self.assertEqual(alice_channel.config[LOCAL].ctn, 1, "alice has incorrect commitment height")
+ self.assertEqual(bob_channel.get_oldest_unrevoked_ctn(LOCAL), 1, "bob has incorrect commitment height")
+ self.assertEqual(alice_channel.get_oldest_unrevoked_ctn(LOCAL), 1, "alice has incorrect commitment height")
# Both commitment transactions should have three outputs, and one of
# them should be exactly the amount of the HTLC.
- alice_ctx = alice_channel.pending_commitment(LOCAL)
- bob_ctx = bob_channel.pending_commitment(LOCAL)
+ alice_ctx = alice_channel.get_next_commitment(LOCAL)
+ bob_ctx = bob_channel.get_next_commitment(LOCAL)
self.assertEqual(len(alice_ctx.outputs()), 3, "alice should have three commitment outputs, instead have %s"% len(alice_ctx.outputs()))
self.assertEqual(len(bob_ctx.outputs()), 3, "bob should have three commitment outputs, instead have %s"% len(bob_ctx.outputs()))
self.assertOutputExistsByValue(alice_ctx, htlc.amount_msat // 1000)
@@ -415,7 +406,6 @@ class TestChannel(unittest.TestCase):
preimage = self.paymentPreimage
bob_channel.settle_htlc(preimage, self.bobHtlcIndex)
- #self.assertEqual(alice_channel.remote_commitment.outputs(), alice_channel.current_commitment(REMOTE).outputs())
alice_channel.receive_htlc_settle(preimage, self.aliceHtlcIndex)
tx3 = str(alice_channel.force_close_tx())
@@ -426,7 +416,7 @@ class TestChannel(unittest.TestCase):
self.assertEqual(len(bobHtlcSigs2), 0)
self.assertEqual(alice_channel.hm.htlcs_by_direction(REMOTE, RECEIVED), [htlc])
- self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, alice_channel.config[REMOTE].ctn), [htlc])
+ self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, alice_channel.get_oldest_unrevoked_ctn(REMOTE)), [htlc])
self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [htlc])
self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 2), [htlc])
@@ -440,8 +430,8 @@ class TestChannel(unittest.TestCase):
self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 1), [])
self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 2), [])
- alice_ctx_bob_version = bob_channel.pending_commitment(REMOTE).outputs()
- alice_ctx_alice_version = alice_channel.pending_commitment(LOCAL).outputs()
+ alice_ctx_bob_version = bob_channel.get_latest_commitment(REMOTE).outputs()
+ alice_ctx_alice_version = alice_channel.get_next_commitment(LOCAL).outputs()
self.assertEqual(alice_ctx_alice_version, alice_ctx_bob_version)
alice_channel.receive_new_commitment(bobSig2, bobHtlcSigs2)
@@ -450,14 +440,13 @@ class TestChannel(unittest.TestCase):
self.assertNotEqual(tx3, tx4)
self.assertEqual(alice_channel.balance(LOCAL), 500000000000)
- self.assertEqual(1, alice_channel.config[LOCAL].ctn)
+ self.assertEqual(1, alice_channel.get_oldest_unrevoked_ctn(LOCAL))
self.assertEqual(len(alice_channel.included_htlcs(LOCAL, RECEIVED, ctn=2)), 0)
aliceRevocation2, _ = alice_channel.revoke_current_commitment()
alice_channel.serialize()
aliceSig2, aliceHtlcSigs2 = alice_channel.sign_next_commitment()
self.assertEqual(aliceHtlcSigs2, [], "alice should generate no htlc signatures")
- self.assertEqual(len(bob_channel.current_commitment(LOCAL).outputs()), 3)
- #self.assertEqual(len(bob_channel.pending_commitment(LOCAL).outputs()), 3)
+ self.assertEqual(len(bob_channel.get_latest_commitment(LOCAL).outputs()), 3)
bob_channel.receive_revocation(aliceRevocation2)
bob_channel.serialize()
@@ -478,14 +467,14 @@ class TestChannel(unittest.TestCase):
self.assertEqual(alice_channel.total_msat(RECEIVED), 0, "alice satoshis received incorrect")
self.assertEqual(bob_channel.total_msat(RECEIVED), mSatTransferred, "bob satoshis received incorrect")
self.assertEqual(bob_channel.total_msat(SENT), 0, "bob satoshis sent incorrect")
- self.assertEqual(bob_channel.current_height[LOCAL], 2, "bob has incorrect commitment height")
- self.assertEqual(alice_channel.current_height[LOCAL], 2, "alice has incorrect commitment height")
+ self.assertEqual(bob_channel.get_latest_ctn(LOCAL), 2, "bob has incorrect commitment height")
+ self.assertEqual(alice_channel.get_latest_ctn(LOCAL), 2, "alice has incorrect commitment height")
alice_channel.update_fee(100000, True)
- alice_outputs = alice_channel.pending_commitment(REMOTE).outputs()
- old_outputs = bob_channel.pending_commitment(LOCAL).outputs()
+ alice_outputs = alice_channel.get_next_commitment(REMOTE).outputs()
+ old_outputs = bob_channel.get_next_commitment(LOCAL).outputs()
bob_channel.update_fee(100000, False)
- new_outputs = bob_channel.pending_commitment(LOCAL).outputs()
+ new_outputs = bob_channel.get_next_commitment(LOCAL).outputs()
self.assertNotEqual(old_outputs, new_outputs)
self.assertEqual(alice_outputs, new_outputs)
@@ -517,13 +506,13 @@ class TestChannel(unittest.TestCase):
def alice_to_bob_fee_update(self, fee=111):
- aoldctx = self.alice_channel.pending_commitment(REMOTE).outputs()
+ aoldctx = self.alice_channel.get_next_commitment(REMOTE).outputs()
self.alice_channel.update_fee(fee, True)
- anewctx = self.alice_channel.pending_commitment(REMOTE).outputs()
+ anewctx = self.alice_channel.get_next_commitment(REMOTE).outputs()
self.assertNotEqual(aoldctx, anewctx)
- boldctx = self.bob_channel.pending_commitment(LOCAL).outputs()
+ boldctx = self.bob_channel.get_next_commitment(LOCAL).outputs()
self.bob_channel.update_fee(fee, False)
- bnewctx = self.bob_channel.pending_commitment(LOCAL).outputs()
+ bnewctx = self.bob_channel.get_next_commitment(LOCAL).outputs()
self.assertNotEqual(boldctx, bnewctx)
self.assertEqual(anewctx, bnewctx)
return fee
@@ -805,12 +794,12 @@ class TestDust(unittest.TestCase):
'timestamp' : 0,
}
- old_values = [x.value for x in bob_channel.current_commitment(LOCAL).outputs() ]
+ old_values = [x.value for x in bob_channel.get_latest_commitment(LOCAL).outputs() ]
aliceHtlcIndex = alice_channel.add_htlc(htlc).htlc_id
bobHtlcIndex = bob_channel.receive_htlc(htlc).htlc_id
force_state_transition(alice_channel, bob_channel)
- alice_ctx = alice_channel.current_commitment(LOCAL)
- bob_ctx = bob_channel.current_commitment(LOCAL)
+ alice_ctx = alice_channel.get_latest_commitment(LOCAL)
+ bob_ctx = bob_channel.get_latest_commitment(LOCAL)
new_values = [x.value for x in bob_ctx.outputs() ]
self.assertNotEqual(old_values, new_values)
self.assertEqual(len(alice_ctx.outputs()), 3)
@@ -820,7 +809,7 @@ class TestDust(unittest.TestCase):
bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex)
alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex)
force_state_transition(bob_channel, alice_channel)
- self.assertEqual(len(alice_channel.pending_commitment(LOCAL).outputs()), 2)
+ self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2)
self.assertEqual(alice_channel.total_msat(SENT) // 1000, htlcAmt)
def force_state_transition(chanA, chanB):