mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
lnhtlc: merge config and state, remove unnecessary properties
This commit is contained in:
parent
ab991ce207
commit
ab2ca4d018
5 changed files with 290 additions and 302 deletions
|
@ -27,9 +27,9 @@ from .util import PrintError, bh2u, print_error, bfh, log_exceptions
|
|||
from .transaction import Transaction, TxOutput
|
||||
from .lnonion import new_onion_packet, OnionHopsDataSingle, OnionPerHop, decode_onion_error, OnionFailureCode
|
||||
from .lnaddr import lndecode
|
||||
from .lnhtlc import HTLCStateMachine, RevokeAndAck
|
||||
from .lnutil import (Outpoint, ChannelConfig, LocalState,
|
||||
RemoteState, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
|
||||
from .lnhtlc import HTLCStateMachine, RevokeAndAck, htlcsum
|
||||
from .lnutil import (Outpoint, LocalConfig, ChannelConfig,
|
||||
RemoteConfig, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
|
||||
funding_output_script, get_ecdh, get_per_commitment_secret_from_seed,
|
||||
secret_to_pubkey, LNPeerAddr, PaymentFailure, LnLocalFeatures,
|
||||
LOCAL, REMOTE, HTLCOwner, generate_keypair, LnKeyFamily,
|
||||
|
@ -480,7 +480,7 @@ class Peer(PrintError):
|
|||
def on_announcement_signatures(self, payload):
|
||||
channel_id = payload['channel_id']
|
||||
chan = self.channels[payload['channel_id']]
|
||||
if chan.local_state.was_announced:
|
||||
if chan.config[LOCAL].was_announced:
|
||||
h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan)
|
||||
else:
|
||||
self.announcement_signatures[channel_id].put_nowait(payload)
|
||||
|
@ -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):
|
||||
def make_local_config(self, funding_sat, push_msat, initiator: HTLCOwner, feerate):
|
||||
# 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)
|
||||
|
@ -549,6 +549,10 @@ class Peer(PrintError):
|
|||
max_htlc_value_in_flight_msat=0xffffffffffffffff,
|
||||
max_accepted_htlcs=5,
|
||||
initial_msat=initial_msat,
|
||||
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
|
||||
|
@ -556,9 +560,8 @@ class Peer(PrintError):
|
|||
@log_exceptions
|
||||
async def channel_establishment_flow(self, password, funding_sat, push_msat, temp_channel_id):
|
||||
await self.initialized
|
||||
local_config, per_commitment_secret_seed = self.make_local_config(funding_sat, push_msat, LOCAL)
|
||||
# amounts
|
||||
local_feerate = self.current_feerate_per_kw()
|
||||
feerate = self.current_feerate_per_kw()
|
||||
local_config, per_commitment_secret_seed = self.make_local_config(funding_sat, push_msat, LOCAL, feerate)
|
||||
# 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'))
|
||||
|
@ -569,7 +572,7 @@ class Peer(PrintError):
|
|||
funding_satoshis=funding_sat,
|
||||
push_msat=push_msat,
|
||||
dust_limit_satoshis=local_config.dust_limit_sat,
|
||||
feerate_per_kw=local_feerate,
|
||||
feerate_per_kw=feerate,
|
||||
max_accepted_htlcs=local_config.max_accepted_htlcs,
|
||||
funding_pubkey=local_config.multisig_key.pubkey,
|
||||
revocation_basepoint=local_config.revocation_basepoint.pubkey,
|
||||
|
@ -587,24 +590,33 @@ class Peer(PrintError):
|
|||
if payload.get('error'):
|
||||
raise Exception(payload.get('error'))
|
||||
remote_per_commitment_point = payload['first_per_commitment_point']
|
||||
remote_config=ChannelConfig(
|
||||
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
|
||||
remote_dust_limit_sat = int.from_bytes(payload['dust_limit_satoshis'], byteorder='big')
|
||||
assert remote_dust_limit_sat < 600, remote_dust_limit_sat
|
||||
assert int.from_bytes(payload['htlc_minimum_msat'], 'big') < 600 * 1000
|
||||
remote_max = int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big')
|
||||
assert remote_max >= 198 * 1000 * 1000, remote_max
|
||||
their_revocation_store = RevocationStore()
|
||||
remote_config = RemoteConfig(
|
||||
payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
|
||||
multisig_key=OnlyPubkeyKeypair(payload["funding_pubkey"]),
|
||||
htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
|
||||
delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
|
||||
revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
|
||||
to_self_delay=int.from_bytes(payload['to_self_delay'], byteorder='big'),
|
||||
dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], byteorder='big'),
|
||||
max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'),
|
||||
dust_limit_sat=remote_dust_limit_sat,
|
||||
max_htlc_value_in_flight_msat=remote_max,
|
||||
max_accepted_htlcs=int.from_bytes(payload["max_accepted_htlcs"], 'big'),
|
||||
initial_msat=push_msat
|
||||
initial_msat=push_msat,
|
||||
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,
|
||||
revocation_store=their_revocation_store,
|
||||
)
|
||||
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
|
||||
assert remote_config.dust_limit_sat < 600
|
||||
assert int.from_bytes(payload['htlc_minimum_msat'], 'big') < 600 * 1000
|
||||
assert remote_config.max_htlc_value_in_flight_msat >= 198 * 1000 * 1000, remote_config.max_htlc_value_in_flight_msat
|
||||
self.print_error('remote delay', remote_config.to_self_delay)
|
||||
self.print_error('funding_txn_minimum_depth', funding_txn_minimum_depth)
|
||||
# create funding tx
|
||||
redeem_script = funding_output_script(local_config, remote_config)
|
||||
funding_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script)
|
||||
|
@ -612,38 +624,21 @@ class Peer(PrintError):
|
|||
funding_tx = self.lnworker.wallet.mktx([funding_output], password, self.lnworker.config, 1000)
|
||||
funding_txid = funding_tx.txid()
|
||||
funding_index = funding_tx.outputs().index(funding_output)
|
||||
# compute amounts
|
||||
local_amount = funding_sat*1000 - push_msat
|
||||
remote_amount = push_msat
|
||||
# remote commitment transaction
|
||||
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_index)
|
||||
their_revocation_store = RevocationStore()
|
||||
chan = {
|
||||
"node_id": self.pubkey,
|
||||
"channel_id": channel_id,
|
||||
"short_channel_id": None,
|
||||
"funding_outpoint": Outpoint(funding_txid, funding_index),
|
||||
"local_config": local_config,
|
||||
"remote_config": remote_config,
|
||||
"remote_state": RemoteState(
|
||||
ctn = -1,
|
||||
next_per_commitment_point=remote_per_commitment_point,
|
||||
current_per_commitment_point=None,
|
||||
amount_msat=remote_amount,
|
||||
revocation_store=their_revocation_store,
|
||||
next_htlc_id = 0,
|
||||
feerate=local_feerate
|
||||
),
|
||||
"local_state": LocalState(
|
||||
ctn = -1,
|
||||
"local_config": LocalConfig(
|
||||
**local_config._asdict(),
|
||||
per_commitment_secret_seed=per_commitment_secret_seed,
|
||||
amount_msat=local_amount,
|
||||
next_htlc_id = 0,
|
||||
funding_locked_received = False,
|
||||
was_announced = False,
|
||||
current_commitment_signature = None,
|
||||
current_htlc_signatures = None,
|
||||
feerate=local_feerate
|
||||
),
|
||||
"constraints": ChannelConstraints(capacity=funding_sat, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth),
|
||||
"remote_commitment_to_be_revoked": None,
|
||||
|
@ -665,8 +660,8 @@ class Peer(PrintError):
|
|||
success, _txid = await self.network.broadcast_transaction(funding_tx)
|
||||
assert success, success
|
||||
m.remote_commitment_to_be_revoked = m.pending_remote_commitment
|
||||
m.remote_state = m.remote_state._replace(ctn=0)
|
||||
m.local_state = m.local_state._replace(ctn=0, current_commitment_signature=remote_sig)
|
||||
m.config[REMOTE] = m.config[REMOTE]._replace(ctn=0)
|
||||
m.config[LOCAL] = m.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig)
|
||||
m.set_state('OPENING')
|
||||
return m
|
||||
|
||||
|
@ -677,21 +672,10 @@ class Peer(PrintError):
|
|||
raise Exception('wrong chain_hash')
|
||||
funding_sat = int.from_bytes(payload['funding_satoshis'], 'big')
|
||||
push_msat = int.from_bytes(payload['push_msat'], 'big')
|
||||
feerate = int.from_bytes(payload['feerate_per_kw'], 'big')
|
||||
|
||||
remote_config = ChannelConfig(
|
||||
payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
|
||||
multisig_key=OnlyPubkeyKeypair(payload['funding_pubkey']),
|
||||
htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
|
||||
delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
|
||||
revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
|
||||
to_self_delay=int.from_bytes(payload['to_self_delay'], 'big'),
|
||||
dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], 'big'),
|
||||
max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'),
|
||||
max_accepted_htlcs=int.from_bytes(payload['max_accepted_htlcs'], 'big'),
|
||||
initial_msat=funding_sat * 1000 - push_msat,
|
||||
)
|
||||
temp_chan_id = payload['temporary_channel_id']
|
||||
local_config, per_commitment_secret_seed = self.make_local_config(funding_sat * 1000, push_msat, REMOTE)
|
||||
local_config, per_commitment_secret_seed = self.make_local_config(funding_sat * 1000, push_msat, REMOTE, feerate)
|
||||
|
||||
# for the first commitment transaction
|
||||
per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, RevocationStore.START_INDEX)
|
||||
|
@ -719,33 +703,39 @@ class Peer(PrintError):
|
|||
funding_txid = bh2u(funding_created['funding_txid'][::-1])
|
||||
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_idx)
|
||||
their_revocation_store = RevocationStore()
|
||||
local_feerate = int.from_bytes(payload['feerate_per_kw'], 'big')
|
||||
remote_balance_sat = funding_sat * 1000 - push_msat
|
||||
chan = {
|
||||
"node_id": self.pubkey,
|
||||
"channel_id": channel_id,
|
||||
"short_channel_id": None,
|
||||
"funding_outpoint": Outpoint(funding_txid, funding_idx),
|
||||
"local_config": local_config,
|
||||
"remote_config": remote_config,
|
||||
"remote_state": RemoteState(
|
||||
"remote_config": RemoteConfig(
|
||||
payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
|
||||
multisig_key=OnlyPubkeyKeypair(payload['funding_pubkey']),
|
||||
htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
|
||||
delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
|
||||
revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
|
||||
to_self_delay=int.from_bytes(payload['to_self_delay'], 'big'),
|
||||
dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], 'big'),
|
||||
max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'),
|
||||
max_accepted_htlcs=int.from_bytes(payload['max_accepted_htlcs'], 'big'),
|
||||
initial_msat=remote_balance_sat,
|
||||
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,
|
||||
amount_msat=remote_config.initial_msat,
|
||||
revocation_store=their_revocation_store,
|
||||
next_htlc_id = 0,
|
||||
feerate=local_feerate
|
||||
),
|
||||
"local_state": LocalState(
|
||||
ctn = -1,
|
||||
"local_config": LocalConfig(
|
||||
**local_config._asdict(),
|
||||
per_commitment_secret_seed=per_commitment_secret_seed,
|
||||
amount_msat=local_config.initial_msat,
|
||||
next_htlc_id = 0,
|
||||
funding_locked_received = False,
|
||||
was_announced = False,
|
||||
current_commitment_signature = None,
|
||||
current_htlc_signatures = None,
|
||||
feerate=local_feerate
|
||||
),
|
||||
"constraints": ChannelConstraints(capacity=funding_sat, is_initiator=False, funding_txn_minimum_depth=min_depth),
|
||||
"remote_commitment_to_be_revoked": None,
|
||||
|
@ -762,8 +752,8 @@ class Peer(PrintError):
|
|||
))
|
||||
m.set_state('OPENING')
|
||||
m.remote_commitment_to_be_revoked = m.pending_remote_commitment
|
||||
m.remote_state = m.remote_state._replace(ctn=0)
|
||||
m.local_state = m.local_state._replace(ctn=0, current_commitment_signature=remote_sig)
|
||||
m.config[REMOTE] = m.config[REMOTE]._replace(ctn=0)
|
||||
m.config[LOCAL] = m.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig)
|
||||
self.lnworker.save_channel(m)
|
||||
self.lnwatcher.watch_channel(m.get_funding_address(), m.funding_outpoint.to_str())
|
||||
self.lnworker.on_channels_updated()
|
||||
|
@ -776,7 +766,7 @@ class Peer(PrintError):
|
|||
else:
|
||||
break
|
||||
outp = funding_tx.outputs()[funding_idx]
|
||||
redeem_script = funding_output_script(remote_config, local_config)
|
||||
redeem_script = funding_output_script(m.config[REMOTE], m.config[LOCAL])
|
||||
funding_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script)
|
||||
if outp != TxOutput(bitcoin.TYPE_ADDRESS, funding_address, funding_sat):
|
||||
m.set_state('DISCONNECTED')
|
||||
|
@ -794,12 +784,12 @@ class Peer(PrintError):
|
|||
self.network.trigger_callback('channel', chan)
|
||||
self.send_message(gen_msg("channel_reestablish",
|
||||
channel_id=chan_id,
|
||||
next_local_commitment_number=chan.local_state.ctn+1,
|
||||
next_remote_revocation_number=chan.remote_state.ctn
|
||||
next_local_commitment_number=chan.config[LOCAL].ctn+1,
|
||||
next_remote_revocation_number=chan.config[REMOTE].ctn
|
||||
))
|
||||
await self.channel_reestablished[chan_id]
|
||||
chan.set_state('OPENING')
|
||||
if chan.local_state.funding_locked_received and chan.short_channel_id:
|
||||
if chan.config[LOCAL].funding_locked_received and chan.short_channel_id:
|
||||
self.mark_open(chan)
|
||||
self.network.trigger_callback('channel', chan)
|
||||
|
||||
|
@ -822,24 +812,24 @@ class Peer(PrintError):
|
|||
channel_reestablish_msg = payload
|
||||
# compare remote ctns
|
||||
remote_ctn = int.from_bytes(channel_reestablish_msg["next_local_commitment_number"], 'big')
|
||||
if remote_ctn != chan.remote_state.ctn + 1:
|
||||
self.print_error("expected remote ctn {}, got {}".format(chan.remote_state.ctn + 1, remote_ctn))
|
||||
if remote_ctn != chan.config[REMOTE].ctn + 1:
|
||||
self.print_error("expected remote ctn {}, got {}".format(chan.config[REMOTE].ctn + 1, remote_ctn))
|
||||
# TODO iff their ctn is lower than ours, we should force close instead
|
||||
try_to_get_remote_to_force_close_with_their_latest()
|
||||
return
|
||||
# compare local ctns
|
||||
local_ctn = int.from_bytes(channel_reestablish_msg["next_remote_revocation_number"], 'big')
|
||||
if local_ctn != chan.local_state.ctn:
|
||||
self.print_error("expected local ctn {}, got {}".format(chan.local_state.ctn, local_ctn))
|
||||
if local_ctn != chan.config[LOCAL].ctn:
|
||||
self.print_error("expected local ctn {}, got {}".format(chan.config[LOCAL].ctn, local_ctn))
|
||||
# TODO iff their ctn is lower than ours, we should force close instead
|
||||
try_to_get_remote_to_force_close_with_their_latest()
|
||||
return
|
||||
# compare per commitment points (needs data_protect option)
|
||||
their_pcp = channel_reestablish_msg.get("my_current_per_commitment_point", None)
|
||||
if their_pcp is not None:
|
||||
our_pcp = chan.remote_state.current_per_commitment_point
|
||||
our_pcp = chan.config[REMOTE].current_per_commitment_point
|
||||
if our_pcp is None:
|
||||
our_pcp = chan.remote_state.next_per_commitment_point
|
||||
our_pcp = chan.config[REMOTE].next_per_commitment_point
|
||||
if our_pcp != their_pcp:
|
||||
self.print_error("Remote PCP mismatch: {} {}".format(bh2u(our_pcp), bh2u(their_pcp)))
|
||||
# FIXME ...what now?
|
||||
|
@ -852,10 +842,10 @@ class Peer(PrintError):
|
|||
channel_id = chan.channel_id
|
||||
per_commitment_secret_index = RevocationStore.START_INDEX - 1
|
||||
per_commitment_point_second = secret_to_pubkey(int.from_bytes(
|
||||
get_per_commitment_secret_from_seed(chan.local_state.per_commitment_secret_seed, per_commitment_secret_index), 'big'))
|
||||
get_per_commitment_secret_from_seed(chan.config[LOCAL].per_commitment_secret_seed, per_commitment_secret_index), 'big'))
|
||||
# note: if funding_locked was not yet received, we might send it multiple times
|
||||
self.send_message(gen_msg("funding_locked", channel_id=channel_id, next_per_commitment_point=per_commitment_point_second))
|
||||
if chan.local_state.funding_locked_received:
|
||||
if chan.config[LOCAL].funding_locked_received:
|
||||
self.mark_open(chan)
|
||||
|
||||
def on_funding_locked(self, payload):
|
||||
|
@ -864,13 +854,13 @@ class Peer(PrintError):
|
|||
if not chan:
|
||||
print(self.channels)
|
||||
raise Exception("Got unknown funding_locked", channel_id)
|
||||
if not chan.local_state.funding_locked_received:
|
||||
our_next_point = chan.remote_state.next_per_commitment_point
|
||||
if not chan.config[LOCAL].funding_locked_received:
|
||||
our_next_point = chan.config[REMOTE].next_per_commitment_point
|
||||
their_next_point = payload["next_per_commitment_point"]
|
||||
new_remote_state = chan.remote_state._replace(next_per_commitment_point=their_next_point, current_per_commitment_point=our_next_point)
|
||||
new_local_state = chan.local_state._replace(funding_locked_received = True)
|
||||
chan.remote_state=new_remote_state
|
||||
chan.local_state=new_local_state
|
||||
new_remote_state = chan.config[REMOTE]._replace(next_per_commitment_point=their_next_point, current_per_commitment_point=our_next_point)
|
||||
new_local_state = chan.config[LOCAL]._replace(funding_locked_received = True)
|
||||
chan.config[REMOTE]=new_remote_state
|
||||
chan.config[LOCAL]=new_local_state
|
||||
self.lnworker.save_channel(chan)
|
||||
if chan.short_channel_id:
|
||||
self.mark_open(chan)
|
||||
|
@ -881,11 +871,11 @@ class Peer(PrintError):
|
|||
|
||||
Runs on the Network thread.
|
||||
"""
|
||||
if not chan.local_state.was_announced and funding_tx_depth >= 6:
|
||||
if not chan.config[LOCAL].was_announced and funding_tx_depth >= 6:
|
||||
# don't announce our channels
|
||||
# FIXME should this be a field in chan.local_state maybe?
|
||||
return
|
||||
chan.local_state=chan.local_state._replace(was_announced=True)
|
||||
chan.config[LOCAL]=chan.config[LOCAL]._replace(was_announced=True)
|
||||
coro = self.handle_announcements(chan)
|
||||
self.lnworker.save_channel(chan)
|
||||
asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
|
||||
|
@ -896,7 +886,7 @@ class Peer(PrintError):
|
|||
announcement_signatures_msg = await self.announcement_signatures[chan.channel_id].get()
|
||||
remote_node_sig = announcement_signatures_msg["node_signature"]
|
||||
remote_bitcoin_sig = announcement_signatures_msg["bitcoin_signature"]
|
||||
if not ecc.verify_signature(chan.remote_config.multisig_key.pubkey, remote_bitcoin_sig, h):
|
||||
if not ecc.verify_signature(chan.config[REMOTE].multisig_key.pubkey, remote_bitcoin_sig, h):
|
||||
raise Exception("bitcoin_sig invalid in announcement_signatures")
|
||||
if not ecc.verify_signature(self.pubkey, remote_node_sig, h):
|
||||
raise Exception("node_sig invalid in announcement_signatures")
|
||||
|
@ -904,7 +894,7 @@ class Peer(PrintError):
|
|||
node_sigs = [local_node_sig, remote_node_sig]
|
||||
bitcoin_sigs = [local_bitcoin_sig, remote_bitcoin_sig]
|
||||
node_ids = [privkey_to_pubkey(self.privkey), self.pubkey]
|
||||
bitcoin_keys = [chan.local_config.multisig_key.pubkey, chan.remote_config.multisig_key.pubkey]
|
||||
bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey, chan.config[REMOTE].multisig_key.pubkey]
|
||||
|
||||
if node_ids[0] > node_ids[1]:
|
||||
node_sigs.reverse()
|
||||
|
@ -935,14 +925,14 @@ class Peer(PrintError):
|
|||
if chan.get_state() == "OPEN":
|
||||
return
|
||||
# NOTE: even closed channels will be temporarily marked "OPEN"
|
||||
assert chan.local_state.funding_locked_received
|
||||
assert chan.config[LOCAL].funding_locked_received
|
||||
chan.set_state("OPEN")
|
||||
self.network.trigger_callback('channel', chan)
|
||||
# add channel to database
|
||||
pubkey_ours = self.lnworker.node_keypair.pubkey
|
||||
pubkey_theirs = self.pubkey
|
||||
node_ids = [pubkey_theirs, pubkey_ours]
|
||||
bitcoin_keys = [chan.local_config.multisig_key.pubkey, chan.remote_config.multisig_key.pubkey]
|
||||
bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey, chan.config[REMOTE].multisig_key.pubkey]
|
||||
sorted_node_ids = list(sorted(node_ids))
|
||||
if sorted_node_ids != node_ids:
|
||||
node_ids = sorted_node_ids
|
||||
|
@ -977,8 +967,8 @@ class Peer(PrintError):
|
|||
|
||||
def send_announcement_signatures(self, chan):
|
||||
|
||||
bitcoin_keys = [chan.local_config.multisig_key.pubkey,
|
||||
chan.remote_config.multisig_key.pubkey]
|
||||
bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey,
|
||||
chan.config[REMOTE].multisig_key.pubkey]
|
||||
|
||||
node_ids = [privkey_to_pubkey(self.privkey),
|
||||
self.pubkey]
|
||||
|
@ -1000,7 +990,7 @@ class Peer(PrintError):
|
|||
)
|
||||
to_hash = chan_ann[256+2:]
|
||||
h = bitcoin.Hash(to_hash)
|
||||
bitcoin_signature = ecc.ECPrivkey(chan.local_config.multisig_key.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
|
||||
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
|
||||
node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
|
||||
self.send_message(gen_msg("announcement_signatures",
|
||||
channel_id=chan.channel_id,
|
||||
|
@ -1105,10 +1095,10 @@ class Peer(PrintError):
|
|||
# then no other payment can use this channel either.
|
||||
# we need finer blacklisting -- e.g. a blacklist for just this "payment session"?
|
||||
# or blacklist entries could store an msat value and also expire
|
||||
if len(chan.htlcs_in_local) + 1 > chan.remote_config.max_accepted_htlcs:
|
||||
if len(chan.htlcs(LOCAL, only_pending=True)) + 1 > chan.config[REMOTE].max_accepted_htlcs:
|
||||
raise PaymentFailure('too many HTLCs already in channel')
|
||||
if chan.htlcsum(chan.htlcs_in_local) + amount_msat > chan.remote_config.max_htlc_value_in_flight_msat:
|
||||
raise PaymentFailure('HTLC value sum would exceed max allowed: {} msat'.format(chan.remote_config.max_htlc_value_in_flight_msat))
|
||||
if htlcsum(chan.htlcs(LOCAL, only_pending=True)) + amount_msat > chan.config[REMOTE].max_htlc_value_in_flight_msat:
|
||||
raise PaymentFailure('HTLC value sum would exceed max allowed: {} msat'.format(chan.config[REMOTE].max_htlc_value_in_flight_msat))
|
||||
if msat_local < 0:
|
||||
# FIXME what about channel_reserve_satoshis? will the remote fail the channel if we go below? test.
|
||||
raise PaymentFailure('not enough local balance')
|
||||
|
@ -1144,7 +1134,7 @@ class Peer(PrintError):
|
|||
channel_id = chan.channel_id
|
||||
expected_received_msat = int(decoded.amount * bitcoin.COIN * 1000)
|
||||
htlc_id = int.from_bytes(htlc["id"], 'big')
|
||||
assert htlc_id == chan.remote_state.next_htlc_id, (htlc_id, chan.remote_state.next_htlc_id)
|
||||
assert htlc_id == chan.config[REMOTE].next_htlc_id, (htlc_id, chan.config[REMOTE].next_htlc_id)
|
||||
assert chan.get_state() == "OPEN"
|
||||
cltv_expiry = int.from_bytes(htlc["cltv_expiry"], 'big')
|
||||
# TODO verify sanity of their cltv expiry
|
||||
|
@ -1166,7 +1156,7 @@ class Peer(PrintError):
|
|||
self.print_error("commitment_signed", payload)
|
||||
channel_id = payload['channel_id']
|
||||
chan = self.channels[channel_id]
|
||||
chan.local_state=chan.local_state._replace(
|
||||
chan.config[LOCAL]=chan.config[LOCAL]._replace(
|
||||
current_commitment_signature=payload['signature'],
|
||||
current_htlc_signatures=payload['htlc_signature'])
|
||||
self.lnworker.save_channel(chan)
|
||||
|
|
|
@ -10,7 +10,7 @@ from .bitcoin import Hash, TYPE_SCRIPT, TYPE_ADDRESS
|
|||
from .bitcoin import redeem_script_to_address
|
||||
from .crypto import sha256
|
||||
from . import ecc
|
||||
from .lnutil import Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore, EncumberedTransaction
|
||||
from .lnutil import Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore, EncumberedTransaction
|
||||
from .lnutil import get_per_commitment_secret_from_seed
|
||||
from .lnutil import make_commitment_output_to_remote_address, make_commitment_output_to_local_witness_script
|
||||
from .lnutil import secret_to_pubkey, derive_privkey, derive_pubkey, derive_blinded_pubkey, derive_blinded_privkey
|
||||
|
@ -75,59 +75,50 @@ class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash',
|
|||
if 'locked_in' not in kwargs:
|
||||
kwargs['locked_in'] = {LOCAL: None, REMOTE: None}
|
||||
else:
|
||||
kwargs['locked_in'] = {HTLCOwner(int(x)): y for x,y in kwargs['locked_in']}
|
||||
kwargs['locked_in'] = {HTLCOwner(int(x)): y for x,y in kwargs['locked_in'].items()}
|
||||
return super().__new__(cls, **kwargs)
|
||||
|
||||
is_key = lambda k: k.endswith("_basepoint") or k.endswith("_key")
|
||||
|
||||
def maybeDecode(k, v):
|
||||
assert type(v) is not list
|
||||
if k in ["node_id", "channel_id", "short_channel_id", "pubkey", "privkey", "current_per_commitment_point", "next_per_commitment_point", "per_commitment_secret_seed", "current_commitment_signature", "current_htlc_signatures"] and v is not None:
|
||||
return binascii.unhexlify(v)
|
||||
return v
|
||||
|
||||
def decodeAll(v):
|
||||
return {i: maybeDecode(i, j) for i, j in v.items()} if isinstance(v, dict) else v
|
||||
|
||||
def typeWrap(k, v, local):
|
||||
if is_key(k):
|
||||
if local:
|
||||
return Keypair(**v)
|
||||
def decodeAll(d, local):
|
||||
for k, v in d.items():
|
||||
if k == 'revocation_store':
|
||||
yield (k, RevocationStore.from_json_obj(v))
|
||||
elif k.endswith("_basepoint") or k.endswith("_key"):
|
||||
if local:
|
||||
yield (k, Keypair(**dict(decodeAll(v, local))))
|
||||
else:
|
||||
yield (k, OnlyPubkeyKeypair(**dict(decodeAll(v, local))))
|
||||
elif k in ["node_id", "channel_id", "short_channel_id", "pubkey", "privkey", "current_per_commitment_point", "next_per_commitment_point", "per_commitment_secret_seed", "current_commitment_signature", "current_htlc_signatures"] and v is not None:
|
||||
yield (k, binascii.unhexlify(v))
|
||||
else:
|
||||
return OnlyPubkeyKeypair(**v)
|
||||
return v
|
||||
yield (k, v)
|
||||
|
||||
def htlcsum(htlcs):
|
||||
return sum([x.amount_msat for x in htlcs])
|
||||
|
||||
class HTLCStateMachine(PrintError):
|
||||
def diagnostic_name(self):
|
||||
return str(self.name)
|
||||
|
||||
def __init__(self, state, name = None):
|
||||
self.local_config = state["local_config"]
|
||||
if type(self.local_config) is not ChannelConfig:
|
||||
new_local_config = {k: typeWrap(k, decodeAll(v), True) for k, v in self.local_config.items()}
|
||||
self.local_config = ChannelConfig(**new_local_config)
|
||||
assert 'local_state' not in state
|
||||
self.config = {}
|
||||
self.config[LOCAL] = state["local_config"]
|
||||
if type(self.config[LOCAL]) is not LocalConfig:
|
||||
conf = dict(decodeAll(self.config[LOCAL], True))
|
||||
self.config[LOCAL] = LocalConfig(**conf)
|
||||
assert type(self.config[LOCAL].htlc_basepoint.privkey) is bytes
|
||||
|
||||
self.remote_config = state["remote_config"]
|
||||
if type(self.remote_config) is not ChannelConfig:
|
||||
new_remote_config = {k: typeWrap(k, decodeAll(v), False) for k, v in self.remote_config.items()}
|
||||
self.remote_config = ChannelConfig(**new_remote_config)
|
||||
self.config[REMOTE] = state["remote_config"]
|
||||
if type(self.config[REMOTE]) is not RemoteConfig:
|
||||
conf = dict(decodeAll(self.config[REMOTE], False))
|
||||
self.config[REMOTE] = RemoteConfig(**conf)
|
||||
assert type(self.config[REMOTE].htlc_basepoint.pubkey) is bytes
|
||||
|
||||
self.local_state = state["local_state"]
|
||||
if type(self.local_state) is not LocalState:
|
||||
self.local_state = LocalState(**decodeAll(self.local_state))
|
||||
|
||||
self.remote_state = state["remote_state"]
|
||||
if type(self.remote_state) is not RemoteState:
|
||||
self.remote_state = RemoteState(**decodeAll(self.remote_state))
|
||||
|
||||
if type(self.remote_state.revocation_store) is not RevocationStore:
|
||||
self.remote_state = self.remote_state._replace(revocation_store = RevocationStore.from_json_obj(self.remote_state.revocation_store))
|
||||
|
||||
self.channel_id = maybeDecode("channel_id", state["channel_id"]) if type(state["channel_id"]) is not bytes else state["channel_id"]
|
||||
self.constraints = ChannelConstraints(**decodeAll(state["constraints"])) if type(state["constraints"]) is not ChannelConstraints else state["constraints"]
|
||||
self.funding_outpoint = Outpoint(**decodeAll(state["funding_outpoint"])) if type(state["funding_outpoint"]) is not Outpoint else state["funding_outpoint"]
|
||||
self.node_id = maybeDecode("node_id", state["node_id"]) if type(state["node_id"]) is not bytes else state["node_id"]
|
||||
self.short_channel_id = maybeDecode("short_channel_id", state["short_channel_id"]) if type(state["short_channel_id"]) is not bytes else state["short_channel_id"]
|
||||
self.channel_id = bfh(state["channel_id"]) if type(state["channel_id"]) not in (bytes, type(None)) else state["channel_id"]
|
||||
self.constraints = ChannelConstraints(**state["constraints"]) if type(state["constraints"]) is not ChannelConstraints else state["constraints"]
|
||||
self.funding_outpoint = Outpoint(**dict(decodeAll(state["funding_outpoint"], False))) if type(state["funding_outpoint"]) is not Outpoint else state["funding_outpoint"]
|
||||
self.node_id = bfh(state["node_id"]) if type(state["node_id"]) not in (bytes, type(None)) else state["node_id"]
|
||||
self.short_channel_id = bfh(state["short_channel_id"]) if type(state["short_channel_id"]) not in (bytes, type(None)) else state["short_channel_id"]
|
||||
self.short_channel_id_predicted = self.short_channel_id
|
||||
self.onion_keys = {int(k): bfh(v) for k,v in state['onion_keys'].items()} if 'onion_keys' in state else {}
|
||||
|
||||
|
@ -141,7 +132,7 @@ class HTLCStateMachine(PrintError):
|
|||
for strname, subject in [('remote_log', REMOTE), ('local_log', LOCAL)]:
|
||||
if strname not in state: continue
|
||||
for y in state[strname]:
|
||||
htlc = UpdateAddHtlc(*decodeAll(y))
|
||||
htlc = UpdateAddHtlc(**y)
|
||||
self.log[subject]['adds'][htlc.htlc_id] = htlc
|
||||
|
||||
self.name = name
|
||||
|
@ -172,7 +163,7 @@ class HTLCStateMachine(PrintError):
|
|||
return self._is_funding_txo_spent is False and self._state == 'DISCONNECTED'
|
||||
|
||||
def get_funding_address(self):
|
||||
script = funding_output_script(self.local_config, self.remote_config)
|
||||
script = funding_output_script(self.config[LOCAL], self.config[REMOTE])
|
||||
return redeem_script_to_address('p2wsh', script)
|
||||
|
||||
def add_htlc(self, htlc):
|
||||
|
@ -181,10 +172,10 @@ class HTLCStateMachine(PrintError):
|
|||
should be called when preparing to send an outgoing HTLC.
|
||||
"""
|
||||
assert type(htlc) is dict
|
||||
htlc = UpdateAddHtlc(**htlc, htlc_id=self.local_state.next_htlc_id)
|
||||
htlc = UpdateAddHtlc(**htlc, htlc_id=self.config[LOCAL].next_htlc_id)
|
||||
self.log[LOCAL]['adds'][htlc.htlc_id] = htlc
|
||||
self.print_error("add_htlc")
|
||||
self.local_state=self.local_state._replace(next_htlc_id=htlc.htlc_id + 1)
|
||||
self.config[LOCAL]=self.config[LOCAL]._replace(next_htlc_id=htlc.htlc_id + 1)
|
||||
return htlc.htlc_id
|
||||
|
||||
def receive_htlc(self, htlc):
|
||||
|
@ -194,10 +185,10 @@ class HTLCStateMachine(PrintError):
|
|||
party.
|
||||
"""
|
||||
assert type(htlc) is dict
|
||||
htlc = UpdateAddHtlc(**htlc, htlc_id = self.remote_state.next_htlc_id)
|
||||
htlc = UpdateAddHtlc(**htlc, htlc_id = self.config[REMOTE].next_htlc_id)
|
||||
self.log[REMOTE]['adds'][htlc.htlc_id] = htlc
|
||||
self.print_error("receive_htlc")
|
||||
self.remote_state=self.remote_state._replace(next_htlc_id=htlc.htlc_id + 1)
|
||||
self.config[REMOTE]=self.config[REMOTE]._replace(next_htlc_id=htlc.htlc_id + 1)
|
||||
return htlc.htlc_id
|
||||
|
||||
def sign_next_commitment(self):
|
||||
|
@ -215,15 +206,15 @@ class HTLCStateMachine(PrintError):
|
|||
"""
|
||||
for htlc in self.log[LOCAL]['adds'].values():
|
||||
if htlc.locked_in[LOCAL] is None:
|
||||
htlc.locked_in[LOCAL] = self.local_state.ctn
|
||||
htlc.locked_in[LOCAL] = self.config[LOCAL].ctn
|
||||
self.print_error("sign_next_commitment")
|
||||
|
||||
pending_remote_commitment = self.pending_remote_commitment
|
||||
sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.local_config, self.remote_config)
|
||||
sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.config[LOCAL], self.config[REMOTE])
|
||||
|
||||
their_remote_htlc_privkey_number = derive_privkey(
|
||||
int.from_bytes(self.local_config.htlc_basepoint.privkey, 'big'),
|
||||
self.remote_state.next_per_commitment_point)
|
||||
int.from_bytes(self.config[LOCAL].htlc_basepoint.privkey, 'big'),
|
||||
self.config[REMOTE].next_per_commitment_point)
|
||||
their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, 'big')
|
||||
|
||||
for_us = False
|
||||
|
@ -231,7 +222,7 @@ class HTLCStateMachine(PrintError):
|
|||
htlcsigs = []
|
||||
for we_receive, htlcs in zip([True, False], [self.included_htlcs(REMOTE, REMOTE), self.included_htlcs(REMOTE, LOCAL)]):
|
||||
for htlc in htlcs:
|
||||
args = [self.remote_state.next_per_commitment_point, for_us, we_receive, pending_remote_commitment, htlc]
|
||||
args = [self.config[REMOTE].next_per_commitment_point, for_us, we_receive, pending_remote_commitment, htlc]
|
||||
htlc_tx = make_htlc_tx_with_open_channel(self, *args)
|
||||
sig = bfh(htlc_tx.sign_txin(0, their_remote_htlc_privkey))
|
||||
htlc_sig = ecc.sig_string_from_der_sig(sig[:-1])
|
||||
|
@ -265,13 +256,13 @@ class HTLCStateMachine(PrintError):
|
|||
self.print_error("receive_new_commitment")
|
||||
for htlc in self.log[REMOTE]['adds'].values():
|
||||
if htlc.locked_in[REMOTE] is None:
|
||||
htlc.locked_in[REMOTE] = self.remote_state.ctn
|
||||
htlc.locked_in[REMOTE] = self.config[REMOTE].ctn
|
||||
assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes
|
||||
|
||||
pending_local_commitment = self.pending_local_commitment
|
||||
preimage_hex = pending_local_commitment.serialize_preimage(0)
|
||||
pre_hash = Hash(bfh(preimage_hex))
|
||||
if not ecc.verify_signature(self.remote_config.multisig_key.pubkey, sig, pre_hash):
|
||||
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)
|
||||
|
||||
_, this_point, _ = self.points
|
||||
|
@ -280,7 +271,7 @@ class HTLCStateMachine(PrintError):
|
|||
for htlc in htlcs:
|
||||
htlc_tx = make_htlc_tx_with_open_channel(self, this_point, True, we_receive, pending_local_commitment, htlc)
|
||||
pre_hash = Hash(bfh(htlc_tx.serialize_preimage(0)))
|
||||
remote_htlc_pubkey = derive_pubkey(self.remote_config.htlc_basepoint.pubkey, this_point)
|
||||
remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, this_point)
|
||||
for idx, sig in enumerate(htlc_sigs):
|
||||
if ecc.verify_signature(remote_htlc_pubkey, sig, pre_hash):
|
||||
del htlc_sigs[idx]
|
||||
|
@ -314,8 +305,8 @@ class HTLCStateMachine(PrintError):
|
|||
|
||||
last_secret, this_point, next_point = self.points
|
||||
|
||||
new_local_feerate = self.local_state.feerate
|
||||
new_remote_feerate = self.remote_state.feerate
|
||||
new_local_feerate = self.config[LOCAL].feerate
|
||||
new_remote_feerate = self.config[REMOTE].feerate
|
||||
|
||||
for pending_fee in self.fee_mgr[:]:
|
||||
if not self.constraints.is_initiator and pending_fee.had(FUNDEE_SIGNED):
|
||||
|
@ -327,11 +318,11 @@ class HTLCStateMachine(PrintError):
|
|||
self.fee_mgr.remove(pending_fee)
|
||||
print("FEERATE CHANGE COMPLETE (initiator)")
|
||||
|
||||
self.local_state=self.local_state._replace(
|
||||
ctn=self.local_state.ctn + 1,
|
||||
self.config[LOCAL]=self.config[LOCAL]._replace(
|
||||
ctn=self.config[LOCAL].ctn + 1,
|
||||
feerate=new_local_feerate
|
||||
)
|
||||
self.remote_state=self.remote_state._replace(
|
||||
self.config[REMOTE]=self.config[REMOTE]._replace(
|
||||
feerate=new_remote_feerate
|
||||
)
|
||||
|
||||
|
@ -341,13 +332,13 @@ class HTLCStateMachine(PrintError):
|
|||
|
||||
@property
|
||||
def points(self):
|
||||
last_small_num = self.local_state.ctn
|
||||
last_small_num = self.config[LOCAL].ctn
|
||||
this_small_num = last_small_num + 1
|
||||
next_small_num = last_small_num + 2
|
||||
last_secret = get_per_commitment_secret_from_seed(self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - last_small_num)
|
||||
this_secret = get_per_commitment_secret_from_seed(self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - this_small_num)
|
||||
last_secret = get_per_commitment_secret_from_seed(self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - last_small_num)
|
||||
this_secret = get_per_commitment_secret_from_seed(self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - this_small_num)
|
||||
this_point = secret_to_pubkey(int.from_bytes(this_secret, 'big'))
|
||||
next_secret = get_per_commitment_secret_from_seed(self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - next_small_num)
|
||||
next_secret = get_per_commitment_secret_from_seed(self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - next_small_num)
|
||||
next_point = secret_to_pubkey(int.from_bytes(next_secret, 'big'))
|
||||
return last_secret, this_point, next_point
|
||||
|
||||
|
@ -358,13 +349,13 @@ class HTLCStateMachine(PrintError):
|
|||
return
|
||||
outpoint = self.funding_outpoint.to_str()
|
||||
if ours:
|
||||
ctn = self.local_state.ctn + 1
|
||||
ctn = self.config[LOCAL].ctn + 1
|
||||
our_per_commitment_secret = get_per_commitment_secret_from_seed(
|
||||
self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
|
||||
self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
|
||||
our_cur_pcp = ecc.ECPrivkey(our_per_commitment_secret).get_public_key_bytes(compressed=True)
|
||||
encumbered_sweeptx = maybe_create_sweeptx_for_our_ctx_to_local(self, ctx, our_cur_pcp, self.sweep_address)
|
||||
else:
|
||||
their_cur_pcp = self.remote_state.next_per_commitment_point
|
||||
their_cur_pcp = self.config[REMOTE].next_per_commitment_point
|
||||
encumbered_sweeptx = maybe_create_sweeptx_for_their_ctx_to_remote(self, ctx, their_cur_pcp, self.sweep_address)
|
||||
self.lnwatcher.add_sweep_tx(outpoint, ctx.txid(), encumbered_sweeptx)
|
||||
|
||||
|
@ -390,7 +381,7 @@ class HTLCStateMachine(PrintError):
|
|||
"""
|
||||
self.print_error("receive_revocation")
|
||||
|
||||
cur_point = self.remote_state.current_per_commitment_point
|
||||
cur_point = self.config[REMOTE].current_per_commitment_point
|
||||
derived_point = ecc.ECPrivkey(revocation.per_commitment_secret).get_public_key_bytes(compressed=True)
|
||||
if cur_point != derived_point:
|
||||
raise Exception('revoked secret not for current point')
|
||||
|
@ -400,14 +391,14 @@ class HTLCStateMachine(PrintError):
|
|||
# this might break
|
||||
prev_remote_commitment = self.pending_remote_commitment
|
||||
|
||||
self.remote_state.revocation_store.add_next_entry(revocation.per_commitment_secret)
|
||||
self.config[REMOTE].revocation_store.add_next_entry(revocation.per_commitment_secret)
|
||||
self.process_new_revocation_secret(revocation.per_commitment_secret)
|
||||
|
||||
def mark_settled(subject):
|
||||
"""
|
||||
find pending settlements for subject (LOCAL or REMOTE) and mark them settled, return value of settled htlcs
|
||||
"""
|
||||
old_amount = self.htlcsum(self.gen_htlc_indices(subject, False))
|
||||
old_amount = htlcsum(self.htlcs(subject, False))
|
||||
|
||||
for htlc_id in self.log[-subject]['settles']:
|
||||
adds = self.log[subject]['adds']
|
||||
|
@ -415,23 +406,23 @@ class HTLCStateMachine(PrintError):
|
|||
self.settled[subject].append(htlc.amount_msat)
|
||||
self.log[-subject]['settles'].clear()
|
||||
|
||||
return old_amount - self.htlcsum(self.gen_htlc_indices(subject, False))
|
||||
return old_amount - htlcsum(self.htlcs(subject, False))
|
||||
|
||||
sent_this_batch = mark_settled(LOCAL)
|
||||
received_this_batch = mark_settled(REMOTE)
|
||||
|
||||
next_point = self.remote_state.next_per_commitment_point
|
||||
next_point = self.config[REMOTE].next_per_commitment_point
|
||||
|
||||
print("RECEIVED", received_this_batch)
|
||||
print("SENT", sent_this_batch)
|
||||
self.remote_state=self.remote_state._replace(
|
||||
ctn=self.remote_state.ctn + 1,
|
||||
self.config[REMOTE]=self.config[REMOTE]._replace(
|
||||
ctn=self.config[REMOTE].ctn + 1,
|
||||
current_per_commitment_point=next_point,
|
||||
next_per_commitment_point=revocation.next_per_commitment_point,
|
||||
amount_msat=self.remote_state.amount_msat + (sent_this_batch - received_this_batch)
|
||||
amount_msat=self.config[REMOTE].amount_msat + (sent_this_batch - received_this_batch)
|
||||
)
|
||||
self.local_state=self.local_state._replace(
|
||||
amount_msat = self.local_state.amount_msat + (received_this_batch - sent_this_batch)
|
||||
self.config[LOCAL]=self.config[LOCAL]._replace(
|
||||
amount_msat = self.config[LOCAL].amount_msat + (received_this_batch - sent_this_batch)
|
||||
)
|
||||
|
||||
for pending_fee in self.fee_mgr:
|
||||
|
@ -444,43 +435,39 @@ class HTLCStateMachine(PrintError):
|
|||
return received_this_batch, sent_this_batch
|
||||
|
||||
def balance(self, subject):
|
||||
initial = self.local_config.initial_msat if subject == LOCAL else self.remote_config.initial_msat
|
||||
initial = self.config[subject].initial_msat
|
||||
|
||||
initial -= sum(self.settled[subject])
|
||||
initial += sum(self.settled[-subject])
|
||||
|
||||
assert initial == (self.local_state.amount_msat if subject == LOCAL else self.remote_state.amount_msat)
|
||||
assert initial == self.config[subject].amount_msat
|
||||
return initial
|
||||
|
||||
@staticmethod
|
||||
def htlcsum(htlcs):
|
||||
amount_unsettled = 0
|
||||
for x in htlcs:
|
||||
amount_unsettled += x.amount_msat
|
||||
return amount_unsettled
|
||||
|
||||
def amounts(self):
|
||||
remote_settled= self.htlcsum(self.gen_htlc_indices(REMOTE, False))
|
||||
local_settled= self.htlcsum(self.gen_htlc_indices(LOCAL, False))
|
||||
unsettled_local = self.htlcsum(self.gen_htlc_indices(LOCAL, True))
|
||||
unsettled_remote = self.htlcsum(self.gen_htlc_indices(REMOTE, True))
|
||||
remote_msat = self.remote_state.amount_msat -\
|
||||
remote_settled= htlcsum(self.htlcs(REMOTE, False))
|
||||
local_settled= htlcsum(self.htlcs(LOCAL, False))
|
||||
unsettled_local = htlcsum(self.htlcs(LOCAL, True))
|
||||
unsettled_remote = htlcsum(self.htlcs(REMOTE, True))
|
||||
remote_msat = self.config[REMOTE].amount_msat -\
|
||||
unsettled_remote + local_settled - remote_settled
|
||||
local_msat = self.local_state.amount_msat -\
|
||||
local_msat = self.config[LOCAL].amount_msat -\
|
||||
unsettled_local + remote_settled - local_settled
|
||||
return remote_msat, local_msat
|
||||
|
||||
def included_htlcs(self, subject, htlc_initiator):
|
||||
"""
|
||||
return filter of non-dust htlcs for subjects commitment transaction, initiated by given party
|
||||
"""
|
||||
feerate = self.pending_feerate(subject)
|
||||
conf = self.remote_config if subject == REMOTE else self.local_config
|
||||
conf = self.config[subject]
|
||||
weight = HTLC_SUCCESS_WEIGHT if subject != htlc_initiator else HTLC_TIMEOUT_WEIGHT
|
||||
htlcs = self.htlcs_in_local if htlc_initiator == LOCAL else self.htlcs_in_remote
|
||||
htlcs = self.htlcs(htlc_initiator, only_pending=True)
|
||||
fee_for_htlc = lambda htlc: htlc.amount_msat // 1000 - (weight * feerate // 1000)
|
||||
return filter(lambda htlc: fee_for_htlc(htlc) >= conf.dust_limit_sat, htlcs)
|
||||
|
||||
@property
|
||||
def pending_remote_commitment(self):
|
||||
this_point = self.remote_state.next_per_commitment_point
|
||||
this_point = self.config[REMOTE].next_per_commitment_point
|
||||
return self.make_commitment(REMOTE, this_point)
|
||||
|
||||
def pending_feerate(self, subject):
|
||||
|
@ -495,7 +482,7 @@ class HTLCStateMachine(PrintError):
|
|||
|
||||
@property
|
||||
def _committed_feerate(self):
|
||||
return {LOCAL: self.local_state.feerate, REMOTE: self.remote_state.feerate}
|
||||
return {LOCAL: self.config[LOCAL].feerate, REMOTE: self.config[REMOTE].feerate}
|
||||
|
||||
@property
|
||||
def pending_local_commitment(self):
|
||||
|
@ -505,10 +492,9 @@ class HTLCStateMachine(PrintError):
|
|||
def total_msat(self, sub):
|
||||
return sum(self.settled[sub])
|
||||
|
||||
def gen_htlc_indices(self, subject, only_pending):
|
||||
def htlcs(self, subject, only_pending):
|
||||
"""
|
||||
only_pending: require the htlc's settlement to be pending (needs additional signatures/acks)
|
||||
include_settled: include settled (totally done with) htlcs
|
||||
"""
|
||||
update_log = self.log[subject]
|
||||
other_log = self.log[-subject]
|
||||
|
@ -521,16 +507,6 @@ class HTLCStateMachine(PrintError):
|
|||
res.append(htlc)
|
||||
return res
|
||||
|
||||
@property
|
||||
def htlcs_in_local(self):
|
||||
"""in the local log. 'offered by us'"""
|
||||
return self.gen_htlc_indices(LOCAL, True)
|
||||
|
||||
@property
|
||||
def htlcs_in_remote(self):
|
||||
"""in the remote log. 'offered by them'"""
|
||||
return self.gen_htlc_indices(REMOTE, True)
|
||||
|
||||
def settle_htlc(self, preimage, htlc_id):
|
||||
"""
|
||||
SettleHTLC attempts to settle an existing outstanding received HTLC.
|
||||
|
@ -552,7 +528,7 @@ class HTLCStateMachine(PrintError):
|
|||
|
||||
@property
|
||||
def current_height(self):
|
||||
return {LOCAL: self.local_state.ctn, REMOTE: self.remote_state.ctn}
|
||||
return {LOCAL: self.config[LOCAL].ctn, REMOTE: self.config[REMOTE].ctn}
|
||||
|
||||
@property
|
||||
def pending_local_fee(self):
|
||||
|
@ -581,7 +557,7 @@ class HTLCStateMachine(PrintError):
|
|||
for i in self.log[subject]['adds'].values():
|
||||
locked_in = i.locked_in[LOCAL] is not None or i.locked_in[REMOTE] is not None
|
||||
if locked_in:
|
||||
htlcs.append(i)
|
||||
htlcs.append(i._asdict())
|
||||
else:
|
||||
removed.append(i.htlc_id)
|
||||
return htlcs, removed
|
||||
|
@ -593,10 +569,8 @@ class HTLCStateMachine(PrintError):
|
|||
remote_filtered, remote_removed = self.remove_uncommitted_htlcs_from_log(REMOTE)
|
||||
local_filtered, local_removed = self.remove_uncommitted_htlcs_from_log(LOCAL)
|
||||
to_save = {
|
||||
"local_config": self.local_config,
|
||||
"remote_config": self.remote_config,
|
||||
"local_state": self.local_state,
|
||||
"remote_state": self.remote_state,
|
||||
"local_config": self.config[LOCAL],
|
||||
"remote_config": self.config[REMOTE],
|
||||
"channel_id": self.channel_id,
|
||||
"short_channel_id": self.short_channel_id,
|
||||
"constraints": self.constraints,
|
||||
|
@ -613,12 +587,12 @@ class HTLCStateMachine(PrintError):
|
|||
# htlcs number must be monotonically increasing,
|
||||
# so we have to decrease the counter
|
||||
if len(remote_removed) != 0:
|
||||
assert min(remote_removed) < to_save['remote_state'].next_htlc_id
|
||||
to_save['remote_state'] = to_save['remote_state']._replace(next_htlc_id = min(remote_removed))
|
||||
assert min(remote_removed) < to_save['remote_config'].next_htlc_id
|
||||
to_save['remote_config'] = to_save['remote_config']._replace(next_htlc_id = min(remote_removed))
|
||||
|
||||
if len(local_removed) != 0:
|
||||
assert min(local_removed) < to_save['local_state'].next_htlc_id
|
||||
to_save['local_state'] = to_save['local_state']._replace(next_htlc_id = min(local_removed))
|
||||
assert min(local_removed) < to_save['local_config'].next_htlc_id
|
||||
to_save['local_config'] = to_save['local_config']._replace(next_htlc_id = min(local_removed))
|
||||
|
||||
return to_save
|
||||
|
||||
|
@ -652,8 +626,8 @@ class HTLCStateMachine(PrintError):
|
|||
remote_msat, local_msat = self.amounts()
|
||||
assert local_msat >= 0
|
||||
assert remote_msat >= 0
|
||||
this_config = self.remote_config if subject != LOCAL else self.local_config
|
||||
other_config = self.remote_config if subject == LOCAL else self.local_config
|
||||
this_config = self.config[subject]
|
||||
other_config = self.config[-subject]
|
||||
other_htlc_pubkey = derive_pubkey(other_config.htlc_basepoint.pubkey, this_point)
|
||||
this_htlc_pubkey = derive_pubkey(this_config.htlc_basepoint.pubkey, this_point)
|
||||
other_revocation_pubkey = derive_blinded_pubkey(other_config.revocation_basepoint.pubkey, this_point)
|
||||
|
@ -676,12 +650,12 @@ class HTLCStateMachine(PrintError):
|
|||
remote_msat, local_msat = local_msat, remote_msat
|
||||
payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point)
|
||||
return make_commitment(
|
||||
(self.local_state.ctn if subject == LOCAL else self.remote_state.ctn) + 1,
|
||||
self.config[subject].ctn + 1,
|
||||
this_config.multisig_key.pubkey,
|
||||
other_config.multisig_key.pubkey,
|
||||
payment_pubkey,
|
||||
self.local_config.payment_basepoint.pubkey,
|
||||
self.remote_config.payment_basepoint.pubkey,
|
||||
self.config[LOCAL].payment_basepoint.pubkey,
|
||||
self.config[REMOTE].payment_basepoint.pubkey,
|
||||
other_revocation_pubkey,
|
||||
derive_pubkey(this_config.delayed_basepoint.pubkey, this_point),
|
||||
other_config.to_self_delay,
|
||||
|
@ -700,28 +674,28 @@ class HTLCStateMachine(PrintError):
|
|||
fee_sat = self.pending_local_fee
|
||||
|
||||
_, outputs = make_outputs(fee_sat * 1000, True,
|
||||
self.local_state.amount_msat,
|
||||
self.remote_state.amount_msat,
|
||||
self.config[LOCAL].amount_msat,
|
||||
self.config[REMOTE].amount_msat,
|
||||
(TYPE_SCRIPT, bh2u(local_script)),
|
||||
(TYPE_SCRIPT, bh2u(remote_script)),
|
||||
[], self.local_config.dust_limit_sat)
|
||||
[], self.config[LOCAL].dust_limit_sat)
|
||||
|
||||
closing_tx = make_closing_tx(self.local_config.multisig_key.pubkey,
|
||||
self.remote_config.multisig_key.pubkey,
|
||||
self.local_config.payment_basepoint.pubkey,
|
||||
self.remote_config.payment_basepoint.pubkey,
|
||||
closing_tx = make_closing_tx(self.config[LOCAL].multisig_key.pubkey,
|
||||
self.config[REMOTE].multisig_key.pubkey,
|
||||
self.config[LOCAL].payment_basepoint.pubkey,
|
||||
self.config[REMOTE].payment_basepoint.pubkey,
|
||||
# TODO hardcoded we_are_initiator:
|
||||
True, *self.funding_outpoint, self.constraints.capacity,
|
||||
outputs)
|
||||
|
||||
der_sig = bfh(closing_tx.sign_txin(0, self.local_config.multisig_key.privkey))
|
||||
der_sig = bfh(closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey))
|
||||
sig = ecc.sig_string_from_der_sig(der_sig[:-1])
|
||||
return sig, fee_sat
|
||||
|
||||
def maybe_create_sweeptx_for_their_ctx_to_remote(chan, ctx, their_pcp: bytes,
|
||||
sweep_address) -> Optional[EncumberedTransaction]:
|
||||
assert isinstance(their_pcp, bytes)
|
||||
payment_bp_privkey = ecc.ECPrivkey(chan.local_config.payment_basepoint.privkey)
|
||||
payment_bp_privkey = ecc.ECPrivkey(chan.config[LOCAL].payment_basepoint.privkey)
|
||||
our_payment_privkey = derive_privkey(payment_bp_privkey.secret_scalar, their_pcp)
|
||||
our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey)
|
||||
our_payment_pubkey = our_payment_privkey.get_public_key_bytes(compressed=True)
|
||||
|
@ -742,11 +716,11 @@ def maybe_create_sweeptx_for_their_ctx_to_local(chan, ctx, per_commitment_secret
|
|||
sweep_address) -> Optional[EncumberedTransaction]:
|
||||
assert isinstance(per_commitment_secret, bytes)
|
||||
per_commitment_point = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
|
||||
revocation_privkey = derive_blinded_privkey(chan.local_config.revocation_basepoint.privkey,
|
||||
revocation_privkey = derive_blinded_privkey(chan.config[LOCAL].revocation_basepoint.privkey,
|
||||
per_commitment_secret)
|
||||
revocation_pubkey = ecc.ECPrivkey(revocation_privkey).get_public_key_bytes(compressed=True)
|
||||
to_self_delay = chan.local_config.to_self_delay
|
||||
delayed_pubkey = derive_pubkey(chan.remote_config.delayed_basepoint.pubkey,
|
||||
to_self_delay = chan.config[LOCAL].to_self_delay
|
||||
delayed_pubkey = derive_pubkey(chan.config[REMOTE].delayed_basepoint.pubkey,
|
||||
per_commitment_point)
|
||||
witness_script = bh2u(make_commitment_output_to_local_witness_script(
|
||||
revocation_pubkey, to_self_delay, delayed_pubkey))
|
||||
|
@ -768,13 +742,13 @@ def maybe_create_sweeptx_for_their_ctx_to_local(chan, ctx, per_commitment_secret
|
|||
def maybe_create_sweeptx_for_our_ctx_to_local(chan, ctx, our_pcp: bytes,
|
||||
sweep_address) -> Optional[EncumberedTransaction]:
|
||||
assert isinstance(our_pcp, bytes)
|
||||
delayed_bp_privkey = ecc.ECPrivkey(chan.local_config.delayed_basepoint.privkey)
|
||||
delayed_bp_privkey = ecc.ECPrivkey(chan.config[LOCAL].delayed_basepoint.privkey)
|
||||
our_localdelayed_privkey = derive_privkey(delayed_bp_privkey.secret_scalar, our_pcp)
|
||||
our_localdelayed_privkey = ecc.ECPrivkey.from_secret_scalar(our_localdelayed_privkey)
|
||||
our_localdelayed_pubkey = our_localdelayed_privkey.get_public_key_bytes(compressed=True)
|
||||
revocation_pubkey = derive_blinded_pubkey(chan.remote_config.revocation_basepoint.pubkey,
|
||||
revocation_pubkey = derive_blinded_pubkey(chan.config[REMOTE].revocation_basepoint.pubkey,
|
||||
our_pcp)
|
||||
to_self_delay = chan.remote_config.to_self_delay
|
||||
to_self_delay = chan.config[REMOTE].to_self_delay
|
||||
witness_script = bh2u(make_commitment_output_to_local_witness_script(
|
||||
revocation_pubkey, to_self_delay, our_localdelayed_pubkey))
|
||||
to_local_address = redeem_script_to_address('p2wsh', witness_script)
|
||||
|
|
|
@ -20,14 +20,36 @@ HTLC_TIMEOUT_WEIGHT = 663
|
|||
HTLC_SUCCESS_WEIGHT = 703
|
||||
|
||||
Keypair = namedtuple("Keypair", ["pubkey", "privkey"])
|
||||
ChannelConfig = namedtuple("ChannelConfig", [
|
||||
"payment_basepoint", "multisig_key", "htlc_basepoint", "delayed_basepoint", "revocation_basepoint",
|
||||
"to_self_delay", "dust_limit_sat", "max_htlc_value_in_flight_msat", "max_accepted_htlcs", "initial_msat"])
|
||||
OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"])
|
||||
RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_msat", "revocation_store", "current_per_commitment_point", "next_htlc_id", "feerate"])
|
||||
LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_msat", "next_htlc_id", "funding_locked_received", "was_announced", "current_commitment_signature", "current_htlc_signatures", "feerate"])
|
||||
|
||||
common = [
|
||||
('ctn' , int),
|
||||
('amount_msat' , int),
|
||||
('next_htlc_id' , int),
|
||||
('feerate' , int),
|
||||
('payment_basepoint' , Keypair),
|
||||
('multisig_key' , Keypair),
|
||||
('htlc_basepoint' , Keypair),
|
||||
('delayed_basepoint' , Keypair),
|
||||
('revocation_basepoint' , Keypair),
|
||||
('to_self_delay' , int),
|
||||
('dust_limit_sat' , int),
|
||||
('max_htlc_value_in_flight_msat' , int),
|
||||
('max_accepted_htlcs' , int),
|
||||
('initial_msat' , int),
|
||||
]
|
||||
|
||||
ChannelConfig = NamedTuple('ChannelConfig', common)
|
||||
|
||||
LocalConfig = NamedTuple('LocalConfig', common + [
|
||||
('per_commitment_secret_seed', bytes),
|
||||
('funding_locked_received', bool),
|
||||
('was_announced', bool),
|
||||
('current_commitment_signature', bytes),
|
||||
('current_htlc_signatures', List[bytes]),
|
||||
])
|
||||
|
||||
ChannelConstraints = namedtuple("ChannelConstraints", ["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"])
|
||||
|
||||
ScriptHtlc = namedtuple('ScriptHtlc', ['redeem_script', 'htlc'])
|
||||
|
||||
|
@ -88,6 +110,12 @@ class RevocationStore:
|
|||
def __hash__(self):
|
||||
return hash(json.dumps(self.serialize(), sort_keys=True))
|
||||
|
||||
RemoteConfig = NamedTuple('RemoteConfig', common + [
|
||||
('next_per_commitment_point' , bytes),
|
||||
('revocation_store' , RevocationStore),
|
||||
('current_per_commitment_point' , bytes),
|
||||
])
|
||||
|
||||
def count_trailing_zeros(index):
|
||||
""" BOLT-03 (where_to_put_secret) """
|
||||
try:
|
||||
|
@ -243,8 +271,8 @@ def make_received_htlc(revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, p
|
|||
|
||||
def make_htlc_tx_with_open_channel(chan, pcp, for_us, we_receive, commit, htlc):
|
||||
amount_msat, cltv_expiry, payment_hash = htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash
|
||||
conf = chan.local_config if for_us else chan.remote_config
|
||||
other_conf = chan.local_config if not for_us else chan.remote_config
|
||||
conf = chan.config[LOCAL] if for_us else chan.config[REMOTE]
|
||||
other_conf = chan.config[LOCAL] if not for_us else chan.config[REMOTE]
|
||||
|
||||
revocation_pubkey = derive_blinded_pubkey(other_conf.revocation_basepoint.pubkey, pcp)
|
||||
delayedpubkey = derive_pubkey(conf.delayed_basepoint.pubkey, pcp)
|
||||
|
@ -412,8 +440,8 @@ def extract_ctn_from_tx(tx, txin_index: int, funder_payment_basepoint: bytes,
|
|||
return get_obscured_ctn(obs, funder_payment_basepoint, fundee_payment_basepoint)
|
||||
|
||||
def extract_ctn_from_tx_and_chan(tx, chan) -> int:
|
||||
funder_conf = chan.local_config if chan.constraints.is_initiator else chan.remote_config
|
||||
fundee_conf = chan.local_config if not chan.constraints.is_initiator else chan.remote_config
|
||||
funder_conf = chan.config[LOCAL] if chan.constraints.is_initiator else chan.config[REMOTE]
|
||||
fundee_conf = chan.config[LOCAL] if not chan.constraints.is_initiator else chan.config[REMOTE]
|
||||
return extract_ctn_from_tx(tx, txin_index=0,
|
||||
funder_payment_basepoint=funder_conf.payment_basepoint.pubkey,
|
||||
fundee_payment_basepoint=fundee_conf.payment_basepoint.pubkey)
|
||||
|
|
|
@ -116,7 +116,7 @@ class LNWorker(PrintError):
|
|||
|
||||
def save_channel(self, openchannel):
|
||||
assert type(openchannel) is HTLCStateMachine
|
||||
if openchannel.remote_state.next_per_commitment_point == openchannel.remote_state.current_per_commitment_point:
|
||||
if openchannel.config[REMOTE].next_per_commitment_point == openchannel.config[REMOTE].current_per_commitment_point:
|
||||
raise Exception("Tried to save channel with next_point == current_point, this should not happen")
|
||||
with self.lock:
|
||||
self.channels[openchannel.channel_id] = openchannel
|
||||
|
@ -350,12 +350,12 @@ class LNWorker(PrintError):
|
|||
chan = self.channels[chan_id]
|
||||
# local_commitment always gives back the next expected local_commitment,
|
||||
# but in this case, we want the current one. So substract one ctn number
|
||||
old_local_state = chan.local_state
|
||||
chan.local_state=chan.local_state._replace(ctn=chan.local_state.ctn - 1)
|
||||
old_local_state = chan.config[LOCAL]
|
||||
chan.config[LOCAL]=chan.config[LOCAL]._replace(ctn=chan.config[LOCAL].ctn - 1)
|
||||
tx = chan.pending_local_commitment
|
||||
chan.local_state = old_local_state
|
||||
tx.sign({bh2u(chan.local_config.multisig_key.pubkey): (chan.local_config.multisig_key.privkey, True)})
|
||||
remote_sig = chan.local_state.current_commitment_signature
|
||||
chan.config[LOCAL] = old_local_state
|
||||
tx.sign({bh2u(chan.config[LOCAL].multisig_key.pubkey): (chan.config[LOCAL].multisig_key.privkey, True)})
|
||||
remote_sig = chan.config[LOCAL].current_commitment_signature
|
||||
remote_sig = der_sig_from_sig_string(remote_sig) + b"\x01"
|
||||
none_idx = tx._inputs[0]["signatures"].index(None)
|
||||
tx.add_signature_to_txin(0, none_idx, bh2u(remote_sig))
|
||||
|
|
|
@ -16,56 +16,52 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate
|
|||
assert remote_amount > 0
|
||||
channel_id, _ = lnbase.channel_id_from_funding_tx(funding_txid, funding_index)
|
||||
their_revocation_store = lnbase.RevocationStore()
|
||||
local_config=lnbase.ChannelConfig(
|
||||
payment_basepoint=privkeys[0],
|
||||
multisig_key=privkeys[1],
|
||||
htlc_basepoint=privkeys[2],
|
||||
delayed_basepoint=privkeys[3],
|
||||
revocation_basepoint=privkeys[4],
|
||||
to_self_delay=l_csv,
|
||||
dust_limit_sat=l_dust,
|
||||
max_htlc_value_in_flight_msat=500000 * 1000,
|
||||
max_accepted_htlcs=5,
|
||||
initial_msat=local_amount,
|
||||
)
|
||||
remote_config=lnbase.ChannelConfig(
|
||||
payment_basepoint=other_pubkeys[0],
|
||||
multisig_key=other_pubkeys[1],
|
||||
htlc_basepoint=other_pubkeys[2],
|
||||
delayed_basepoint=other_pubkeys[3],
|
||||
revocation_basepoint=other_pubkeys[4],
|
||||
to_self_delay=r_csv,
|
||||
dust_limit_sat=r_dust,
|
||||
max_htlc_value_in_flight_msat=500000 * 1000,
|
||||
max_accepted_htlcs=5,
|
||||
initial_msat=remote_amount,
|
||||
)
|
||||
|
||||
return {
|
||||
"channel_id":channel_id,
|
||||
"short_channel_id":channel_id[:8],
|
||||
"funding_outpoint":lnbase.Outpoint(funding_txid, funding_index),
|
||||
"local_config":local_config,
|
||||
"remote_config":remote_config,
|
||||
"remote_state":lnbase.RemoteState(
|
||||
"remote_config":lnbase.RemoteConfig(
|
||||
payment_basepoint=other_pubkeys[0],
|
||||
multisig_key=other_pubkeys[1],
|
||||
htlc_basepoint=other_pubkeys[2],
|
||||
delayed_basepoint=other_pubkeys[3],
|
||||
revocation_basepoint=other_pubkeys[4],
|
||||
to_self_delay=r_csv,
|
||||
dust_limit_sat=r_dust,
|
||||
max_htlc_value_in_flight_msat=500000 * 1000,
|
||||
max_accepted_htlcs=5,
|
||||
initial_msat=remote_amount,
|
||||
ctn = 0,
|
||||
next_htlc_id = 0,
|
||||
feerate=local_feerate,
|
||||
amount_msat=remote_amount,
|
||||
|
||||
next_per_commitment_point=nex,
|
||||
current_per_commitment_point=cur,
|
||||
amount_msat=remote_amount,
|
||||
revocation_store=their_revocation_store,
|
||||
next_htlc_id = 0,
|
||||
feerate=local_feerate
|
||||
),
|
||||
"local_state":lnbase.LocalState(
|
||||
"local_config":lnbase.LocalConfig(
|
||||
payment_basepoint=privkeys[0],
|
||||
multisig_key=privkeys[1],
|
||||
htlc_basepoint=privkeys[2],
|
||||
delayed_basepoint=privkeys[3],
|
||||
revocation_basepoint=privkeys[4],
|
||||
to_self_delay=l_csv,
|
||||
dust_limit_sat=l_dust,
|
||||
max_htlc_value_in_flight_msat=500000 * 1000,
|
||||
max_accepted_htlcs=5,
|
||||
initial_msat=local_amount,
|
||||
ctn = 0,
|
||||
per_commitment_secret_seed=seed,
|
||||
amount_msat=local_amount,
|
||||
next_htlc_id = 0,
|
||||
feerate=local_feerate,
|
||||
amount_msat=local_amount,
|
||||
|
||||
per_commitment_secret_seed=seed,
|
||||
funding_locked_received=True,
|
||||
was_announced=False,
|
||||
current_commitment_signature=None,
|
||||
current_htlc_signatures=None,
|
||||
feerate=local_feerate
|
||||
),
|
||||
"constraints":lnbase.ChannelConstraints(capacity=funding_sat, is_initiator=is_initiator, funding_txn_minimum_depth=3),
|
||||
"node_id":other_node_id,
|
||||
|
@ -205,8 +201,8 @@ class TestLNBaseHTLCStateMachine(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.local_state.ctn, 1, "bob has incorrect commitment height")
|
||||
self.assertEqual(alice_channel.local_state.ctn, 1, "alice has incorrect commitment height")
|
||||
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")
|
||||
|
||||
# Both commitment transactions should have three outputs, and one of
|
||||
# them should be exactly the amount of the HTLC.
|
||||
|
@ -275,20 +271,20 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
|
|||
|
||||
bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs)
|
||||
|
||||
self.assertNotEqual(fee, bob_channel.local_state.feerate)
|
||||
self.assertNotEqual(fee, bob_channel.config[LOCAL].feerate)
|
||||
rev, _ = bob_channel.revoke_current_commitment()
|
||||
self.assertEqual(fee, bob_channel.local_state.feerate)
|
||||
self.assertEqual(fee, bob_channel.config[LOCAL].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.local_state.feerate)
|
||||
self.assertNotEqual(fee, alice_channel.config[LOCAL].feerate)
|
||||
rev, _ = alice_channel.revoke_current_commitment()
|
||||
self.assertEqual(fee, alice_channel.local_state.feerate)
|
||||
self.assertEqual(fee, alice_channel.config[LOCAL].feerate)
|
||||
|
||||
bob_channel.receive_revocation(rev)
|
||||
self.assertEqual(fee, bob_channel.remote_state.feerate)
|
||||
self.assertEqual(fee, bob_channel.config[REMOTE].feerate)
|
||||
|
||||
|
||||
def test_UpdateFeeReceiverCommits(self):
|
||||
|
@ -304,20 +300,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.local_state.feerate)
|
||||
self.assertNotEqual(fee, bob_channel.config[LOCAL].feerate)
|
||||
bob_revocation, _ = bob_channel.revoke_current_commitment()
|
||||
self.assertEqual(fee, bob_channel.local_state.feerate)
|
||||
self.assertEqual(fee, bob_channel.config[LOCAL].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.local_state.feerate)
|
||||
self.assertNotEqual(fee, alice_channel.config[LOCAL].feerate)
|
||||
alice_revocation, _ = alice_channel.revoke_current_commitment()
|
||||
self.assertEqual(fee, alice_channel.local_state.feerate)
|
||||
self.assertEqual(fee, alice_channel.config[LOCAL].feerate)
|
||||
|
||||
bob_channel.receive_revocation(alice_revocation)
|
||||
self.assertEqual(fee, bob_channel.remote_state.feerate)
|
||||
self.assertEqual(fee, bob_channel.config[REMOTE].feerate)
|
||||
|
||||
|
||||
|
||||
|
@ -327,7 +323,7 @@ class TestLNHTLCDust(unittest.TestCase):
|
|||
|
||||
paymentPreimage = b"\x01" * 32
|
||||
paymentHash = bitcoin.sha256(paymentPreimage)
|
||||
fee_per_kw = alice_channel.local_state.feerate
|
||||
fee_per_kw = alice_channel.config[LOCAL].feerate
|
||||
self.assertEqual(fee_per_kw, 6000)
|
||||
htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000)
|
||||
self.assertEqual(htlcAmt, 4478)
|
||||
|
|
Loading…
Add table
Reference in a new issue