mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
ln: avoid duplicated htlc filter code, support multiple htlcs better
This commit is contained in:
parent
67734d5db2
commit
a028af9dec
2 changed files with 53 additions and 63 deletions
|
@ -17,6 +17,7 @@ from .lnutil import sign_and_get_sig_string
|
|||
from .lnutil import make_htlc_tx_with_open_channel, make_commitment, make_received_htlc, make_offered_htlc
|
||||
from .lnutil import HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT
|
||||
from .lnutil import funding_output_script, LOCAL, REMOTE, HTLCOwner, make_closing_tx, make_outputs
|
||||
from .lnutil import ScriptHtlc
|
||||
from .transaction import Transaction
|
||||
|
||||
|
||||
|
@ -265,19 +266,10 @@ class HTLCStateMachine(PrintError):
|
|||
|
||||
for_us = False
|
||||
|
||||
feerate = self.pending_feerate(REMOTE)
|
||||
|
||||
htlcsigs = []
|
||||
for we_receive, htlcs in zip([True, False], [self.htlcs_in_remote, self.htlcs_in_local]):
|
||||
assert len(htlcs) <= 1
|
||||
for we_receive, htlcs in zip([True, False], [self.included_htlcs(REMOTE, REMOTE), self.included_htlcs(REMOTE, LOCAL)]):
|
||||
for htlc in htlcs:
|
||||
weight = HTLC_SUCCESS_WEIGHT if we_receive else HTLC_TIMEOUT_WEIGHT
|
||||
fee = feerate // 1000 * weight
|
||||
if htlc.amount_msat // 1000 < self.remote_config.dust_limit_sat + fee:
|
||||
print("value too small, skipping. htlc amt: {}, weight: {}, remote feerate {}, remote dust limit {}".format( htlc.amount_msat, weight, feerate, self.remote_config.dust_limit_sat))
|
||||
continue
|
||||
original_htlc_output_index = 0
|
||||
args = [self.remote_state.next_per_commitment_point, for_us, we_receive, htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash, pending_remote_commitment, original_htlc_output_index]
|
||||
args = [self.remote_state.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])
|
||||
|
@ -293,6 +285,8 @@ class HTLCStateMachine(PrintError):
|
|||
if self.lnwatcher:
|
||||
self.lnwatcher.process_new_offchain_ctx(self, pending_remote_commitment, ours=False)
|
||||
|
||||
htlcsigs.sort()
|
||||
|
||||
return sig_64, htlcsigs
|
||||
|
||||
def receive_new_commitment(self, sig, htlc_sigs):
|
||||
|
@ -322,27 +316,19 @@ class HTLCStateMachine(PrintError):
|
|||
|
||||
_, this_point, _ = self.points
|
||||
|
||||
if len(pending_local_commitment.outputs()) >= 3:
|
||||
print("CHECKING HTLC SIGS")
|
||||
assert len(pending_local_commitment.outputs()) == 3
|
||||
if len(self.htlcs_in_remote) > 0:
|
||||
assert len(self.htlcs_in_remote) == 1
|
||||
we_receive = True
|
||||
htlc = self.htlcs_in_remote[0]
|
||||
elif len(self.htlcs_in_local) > 0:
|
||||
assert len(self.htlcs_in_local) == 1
|
||||
we_receive = False
|
||||
htlc = self.htlcs_in_local[0]
|
||||
else:
|
||||
assert False
|
||||
|
||||
htlc_tx = make_htlc_tx_with_open_channel(self, this_point, True, we_receive, htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash, pending_local_commitment, 0)
|
||||
pre_hash = Hash(bfh(htlc_tx.serialize_preimage(0)))
|
||||
remote_htlc_pubkey = derive_pubkey(self.remote_config.htlc_basepoint.pubkey, this_point)
|
||||
if not ecc.verify_signature(remote_htlc_pubkey, htlc_sigs[0], pre_hash):
|
||||
raise Exception("failed verifying signature an HTLC tx spending from one of our commit tx'es HTLC outputs")
|
||||
|
||||
# TODO check htlc in htlcs_in_local
|
||||
for htlcs, we_receive in [(self.included_htlcs(LOCAL, REMOTE), True), (self.included_htlcs(LOCAL, LOCAL), False)]:
|
||||
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)
|
||||
for idx, sig in enumerate(htlc_sigs):
|
||||
if ecc.verify_signature(remote_htlc_pubkey, sig, pre_hash):
|
||||
del htlc_sigs[idx]
|
||||
break
|
||||
else:
|
||||
raise Exception(f'failed verifying HTLC signatures: {htlc}')
|
||||
if len(htlc_sigs) != 0: # all sigs should have been popped above
|
||||
raise Exception('failed verifying HTLC signatures: invalid amount of correct signatures')
|
||||
|
||||
for pending_fee in self.fee_mgr:
|
||||
if pending_fee.is_proposed():
|
||||
|
@ -513,6 +499,13 @@ class HTLCStateMachine(PrintError):
|
|||
unsettled_local + remote_settled - local_settled
|
||||
return remote_msat, local_msat
|
||||
|
||||
def included_htlcs(self, subject, htlc_initiator):
|
||||
feerate = self.pending_feerate(subject)
|
||||
conf = self.remote_config if subject == REMOTE else self.local_config
|
||||
weight = HTLC_SUCCESS_WEIGHT if subject != htlc_initiator else HTLC_TIMEOUT_WEIGHT
|
||||
return filter(lambda htlc: htlc.amount_msat // 1000 - weight * (feerate // 1000) >= conf.dust_limit_sat,
|
||||
self.htlcs_in_local if htlc_initiator == LOCAL else self.htlcs_in_remote)
|
||||
|
||||
@property
|
||||
def pending_remote_commitment(self):
|
||||
remote_msat, local_msat = self.amounts()
|
||||
|
@ -525,21 +518,15 @@ class HTLCStateMachine(PrintError):
|
|||
local_htlc_pubkey = derive_pubkey(self.local_config.htlc_basepoint.pubkey, this_point)
|
||||
local_revocation_pubkey = derive_blinded_pubkey(self.local_config.revocation_basepoint.pubkey, this_point)
|
||||
|
||||
feerate = self.pending_feerate(REMOTE)
|
||||
|
||||
htlcs_in_local = []
|
||||
for htlc in self.htlcs_in_local:
|
||||
if htlc.amount_msat // 1000 - HTLC_SUCCESS_WEIGHT * (feerate // 1000) < self.remote_config.dust_limit_sat:
|
||||
continue
|
||||
for htlc in self.included_htlcs(REMOTE, LOCAL):
|
||||
htlcs_in_local.append(
|
||||
( make_received_htlc(local_revocation_pubkey, local_htlc_pubkey, remote_htlc_pubkey, htlc.payment_hash, htlc.cltv_expiry), htlc.amount_msat))
|
||||
ScriptHtlc( make_received_htlc(local_revocation_pubkey, local_htlc_pubkey, remote_htlc_pubkey, htlc.payment_hash, htlc.cltv_expiry), htlc))
|
||||
|
||||
htlcs_in_remote = []
|
||||
for htlc in self.htlcs_in_remote:
|
||||
if htlc.amount_msat // 1000 - HTLC_TIMEOUT_WEIGHT * (feerate // 1000) < self.remote_config.dust_limit_sat:
|
||||
continue
|
||||
for htlc in self.included_htlcs(REMOTE, REMOTE):
|
||||
htlcs_in_remote.append(
|
||||
( make_offered_htlc(local_revocation_pubkey, local_htlc_pubkey, remote_htlc_pubkey, htlc.payment_hash), htlc.amount_msat))
|
||||
ScriptHtlc( make_offered_htlc(local_revocation_pubkey, local_htlc_pubkey, remote_htlc_pubkey, htlc.payment_hash), htlc))
|
||||
|
||||
commit = self.make_commitment(self.remote_state.ctn + 1,
|
||||
False, this_point,
|
||||
|
@ -575,18 +562,14 @@ class HTLCStateMachine(PrintError):
|
|||
feerate = self.pending_feerate(LOCAL)
|
||||
|
||||
htlcs_in_local = []
|
||||
for htlc in self.htlcs_in_local:
|
||||
if htlc.amount_msat // 1000 - HTLC_TIMEOUT_WEIGHT * (feerate // 1000) < self.local_config.dust_limit_sat:
|
||||
continue
|
||||
for htlc in self.included_htlcs(LOCAL, LOCAL):
|
||||
htlcs_in_local.append(
|
||||
( make_offered_htlc(remote_revocation_pubkey, remote_htlc_pubkey, local_htlc_pubkey, htlc.payment_hash), htlc.amount_msat))
|
||||
ScriptHtlc( make_offered_htlc(remote_revocation_pubkey, remote_htlc_pubkey, local_htlc_pubkey, htlc.payment_hash), htlc))
|
||||
|
||||
htlcs_in_remote = []
|
||||
for htlc in self.htlcs_in_remote:
|
||||
if htlc.amount_msat // 1000 - HTLC_SUCCESS_WEIGHT * (feerate // 1000) < self.local_config.dust_limit_sat:
|
||||
continue
|
||||
for htlc in self.included_htlcs(LOCAL, REMOTE):
|
||||
htlcs_in_remote.append(
|
||||
( make_received_htlc(remote_revocation_pubkey, remote_htlc_pubkey, local_htlc_pubkey, htlc.payment_hash, htlc.cltv_expiry), htlc.amount_msat))
|
||||
ScriptHtlc( make_received_htlc(remote_revocation_pubkey, remote_htlc_pubkey, local_htlc_pubkey, htlc.payment_hash, htlc.cltv_expiry), htlc))
|
||||
|
||||
commit = self.make_commitment(self.local_state.ctn + 1,
|
||||
True, this_point,
|
||||
|
|
|
@ -25,6 +25,8 @@ LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amo
|
|||
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'])
|
||||
|
||||
class Outpoint(NamedTuple("Outpoint", [('txid', str), ('output_index', int)])):
|
||||
def to_str(self):
|
||||
return "{}:{}".format(self.txid, self.output_index)
|
||||
|
@ -235,7 +237,8 @@ def make_received_htlc(revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, p
|
|||
+ bitcoin.add_number_to_script(cltv_expiry) \
|
||||
+ bytes([opcodes.OP_CLTV, opcodes.OP_DROP, opcodes.OP_CHECKSIG, opcodes.OP_ENDIF, opcodes.OP_ENDIF])
|
||||
|
||||
def make_htlc_tx_with_open_channel(chan, pcp, for_us, we_receive, amount_msat, cltv_expiry, payment_hash, commit, original_htlc_output_index):
|
||||
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
|
||||
|
||||
|
@ -258,8 +261,9 @@ def make_htlc_tx_with_open_channel(chan, pcp, for_us, we_receive, amount_msat, c
|
|||
preimage_script = make_received_htlc(other_revocation_pubkey, other_htlc_pubkey, htlc_pubkey, payment_hash, cltv_expiry)
|
||||
else:
|
||||
preimage_script = make_offered_htlc(other_revocation_pubkey, other_htlc_pubkey, htlc_pubkey, payment_hash)
|
||||
output_idx = commit.htlc_output_indices[htlc.payment_hash]
|
||||
htlc_tx_inputs = make_htlc_tx_inputs(
|
||||
commit.txid(), commit.htlc_output_indices[original_htlc_output_index],
|
||||
commit.txid(), output_idx,
|
||||
revocationpubkey=revocation_pubkey,
|
||||
local_delayedpubkey=delayedpubkey,
|
||||
amount_msat=amount_msat,
|
||||
|
@ -290,20 +294,21 @@ def make_funding_input(local_funding_pubkey: bytes, remote_funding_pubkey: bytes
|
|||
return c_input, payments
|
||||
|
||||
def make_outputs(fee_msat: int, we_pay_fee: bool, local_amount: int, remote_amount: int,
|
||||
local_tupl, remote_tupl, htlcs: List[Tuple[bytes, int]], dust_limit_sat: int) -> Tuple[List[TxOutput], List[TxOutput]]:
|
||||
local_tupl, remote_tupl, htlcs: List[ScriptHtlc], dust_limit_sat: int) -> Tuple[List[TxOutput], List[TxOutput]]:
|
||||
to_local_amt = local_amount - (fee_msat if we_pay_fee else 0)
|
||||
to_local = TxOutput(*local_tupl, to_local_amt // 1000)
|
||||
to_remote_amt = remote_amount - (fee_msat if not we_pay_fee else 0)
|
||||
to_remote = TxOutput(*remote_tupl, to_remote_amt // 1000)
|
||||
c_outputs = [to_local, to_remote]
|
||||
for script, msat_amount in htlcs:
|
||||
c_outputs += [TxOutput(bitcoin.TYPE_ADDRESS,
|
||||
non_htlc_outputs = [to_local, to_remote]
|
||||
htlc_outputs = []
|
||||
for script, htlc in htlcs:
|
||||
htlc_outputs.append(TxOutput(bitcoin.TYPE_ADDRESS,
|
||||
bitcoin.redeem_script_to_address('p2wsh', bh2u(script)),
|
||||
msat_amount // 1000)]
|
||||
htlc.amount_msat // 1000))
|
||||
|
||||
# trim outputs
|
||||
c_outputs_filtered = list(filter(lambda x:x[2]>= dust_limit_sat, c_outputs))
|
||||
return c_outputs, c_outputs_filtered
|
||||
c_outputs_filtered = list(filter(lambda x: x.value >= dust_limit_sat, non_htlc_outputs + htlc_outputs))
|
||||
return htlc_outputs, c_outputs_filtered
|
||||
|
||||
|
||||
def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey,
|
||||
|
@ -331,19 +336,21 @@ def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey,
|
|||
fee = fee // 1000 * 1000
|
||||
we_pay_fee = for_us == we_are_initiator
|
||||
|
||||
c_outputs, c_outputs_filtered = make_outputs(fee, we_pay_fee, local_amount, remote_amount,
|
||||
htlc_outputs, c_outputs_filtered = make_outputs(fee, we_pay_fee, local_amount, remote_amount,
|
||||
(bitcoin.TYPE_ADDRESS, local_address), (bitcoin.TYPE_ADDRESS, remote_address), htlcs, dust_limit_sat)
|
||||
|
||||
assert sum(x[2] for x in c_outputs) <= funding_sat
|
||||
assert sum(x.value for x in c_outputs_filtered) <= funding_sat
|
||||
|
||||
# create commitment tx
|
||||
tx = Transaction.from_io(c_inputs, c_outputs_filtered, locktime=locktime, version=2)
|
||||
|
||||
tx.htlc_output_indices = {}
|
||||
for idx, output in enumerate(c_outputs):
|
||||
assert len(htlcs) == len(htlc_outputs)
|
||||
for script_htlc, output in zip(htlcs, htlc_outputs):
|
||||
if output in tx.outputs():
|
||||
# minus the first two outputs (to_local, to_remote)
|
||||
tx.htlc_output_indices[idx - 2] = tx.outputs().index(output)
|
||||
assert script_htlc.htlc.payment_hash not in tx.htlc_output_indices
|
||||
tx.htlc_output_indices[script_htlc.htlc.payment_hash] = tx.outputs().index(output)
|
||||
|
||||
return tx
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue