Browse Source

ln: do not use mSAT accuracy for commitment fees

regtest_lnd
Janus 7 years ago
committed by SomberNight
parent
commit
9d4bf77ef2
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 10
      electrum/lnhtlc.py
  2. 7
      electrum/lnutil.py
  3. 21
      electrum/tests/test_lnhtlc.py

10
electrum/lnhtlc.py

@ -2,7 +2,7 @@
from collections import namedtuple from collections import namedtuple
import binascii import binascii
import json import json
from .util import bfh, PrintError from .util import bfh, PrintError, bh2u
from .bitcoin import Hash from .bitcoin import Hash
from .bitcoin import redeem_script_to_address from .bitcoin import redeem_script_to_address
from .crypto import sha256 from .crypto import sha256
@ -199,14 +199,16 @@ class HTLCStateMachine(PrintError):
for_us = False for_us = False
feerate = self.pending_remote_feerate
htlcsigs = [] htlcsigs = []
for we_receive, htlcs in zip([True, False], [self.htlcs_in_remote, self.htlcs_in_local]): for we_receive, htlcs in zip([True, False], [self.htlcs_in_remote, self.htlcs_in_local]):
assert len(htlcs) <= 1 assert len(htlcs) <= 1
for htlc in htlcs: for htlc in htlcs:
weight = HTLC_SUCCESS_WEIGHT if we_receive else HTLC_TIMEOUT_WEIGHT weight = HTLC_SUCCESS_WEIGHT if we_receive else HTLC_TIMEOUT_WEIGHT
fee = self.remote_state.feerate // 1000 * weight fee = feerate // 1000 * weight
if htlc.amount_msat // 1000 < self.remote_config.dust_limit_sat + fee: 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, self.remote_state.feerate, self.remote_config.dust_limit_sat)) 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 continue
original_htlc_output_index = 0 original_htlc_output_index = 0
args = [self.remote_state.next_per_commitment_point, for_us, we_receive, htlc.amount_msat + htlc.total_fee, htlc.cltv_expiry, htlc.payment_hash, self.pending_remote_commitment, original_htlc_output_index] args = [self.remote_state.next_per_commitment_point, for_us, we_receive, htlc.amount_msat + htlc.total_fee, htlc.cltv_expiry, htlc.payment_hash, self.pending_remote_commitment, original_htlc_output_index]
@ -244,7 +246,7 @@ class HTLCStateMachine(PrintError):
preimage_hex = self.pending_local_commitment.serialize_preimage(0) preimage_hex = self.pending_local_commitment.serialize_preimage(0)
pre_hash = Hash(bfh(preimage_hex)) 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.remote_config.multisig_key.pubkey, sig, pre_hash):
raise Exception('failed verifying signature of our updated commitment transaction: ' + str(sig)) raise Exception('failed verifying signature of our updated commitment transaction: ' + bh2u(sig) + ' preimage is ' + preimage_hex)
_, this_point, _ = self.points _, this_point, _ = self.points

7
electrum/lnutil.py

@ -153,6 +153,7 @@ def make_htlc_tx_output(amount_msat, local_feerate, revocationpubkey, local_dela
p2wsh = bitcoin.redeem_script_to_address('p2wsh', bh2u(script)) p2wsh = bitcoin.redeem_script_to_address('p2wsh', bh2u(script))
weight = HTLC_SUCCESS_WEIGHT if success else HTLC_TIMEOUT_WEIGHT weight = HTLC_SUCCESS_WEIGHT if success else HTLC_TIMEOUT_WEIGHT
fee = local_feerate * weight fee = local_feerate * weight
fee = fee // 1000 * 1000
final_amount_sat = (amount_msat - fee) // 1000 final_amount_sat = (amount_msat - fee) // 1000
assert final_amount_sat > 0, final_amount_sat assert final_amount_sat > 0, final_amount_sat
output = (bitcoin.TYPE_ADDRESS, p2wsh, final_amount_sat) output = (bitcoin.TYPE_ADDRESS, p2wsh, final_amount_sat)
@ -242,7 +243,7 @@ def make_htlc_tx_with_open_channel(chan, pcp, for_us, we_receive, amount_msat, c
is_htlc_success = for_us == we_receive is_htlc_success = for_us == we_receive
htlc_tx_output = make_htlc_tx_output( htlc_tx_output = make_htlc_tx_output(
amount_msat = amount_msat, amount_msat = amount_msat,
local_feerate = chan.local_state.feerate if for_us else chan.remote_state.feerate, local_feerate = chan.pending_local_feerate if for_us else chan.pending_remote_feerate,
revocationpubkey=revocation_pubkey, revocationpubkey=revocation_pubkey,
local_delayedpubkey=delayedpubkey, local_delayedpubkey=delayedpubkey,
success = is_htlc_success, success = is_htlc_success,
@ -295,13 +296,11 @@ def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey,
remote_address = make_commitment_output_to_remote_address(remote_payment_pubkey) remote_address = make_commitment_output_to_remote_address(remote_payment_pubkey)
# TODO trim htlc outputs here while also considering 2nd stage htlc transactions # TODO trim htlc outputs here while also considering 2nd stage htlc transactions
fee = local_feerate * overall_weight(len(htlcs)) fee = local_feerate * overall_weight(len(htlcs))
assert type(fee) is int fee = fee // 1000 * 1000
we_pay_fee = for_us == we_are_initiator we_pay_fee = for_us == we_are_initiator
to_local_amt = local_amount - (fee if we_pay_fee else 0) to_local_amt = local_amount - (fee if we_pay_fee else 0)
assert type(to_local_amt) is int
to_local = (bitcoin.TYPE_ADDRESS, local_address, to_local_amt // 1000) to_local = (bitcoin.TYPE_ADDRESS, local_address, to_local_amt // 1000)
to_remote_amt = remote_amount - (fee if not we_pay_fee else 0) to_remote_amt = remote_amount - (fee if not we_pay_fee else 0)
assert type(to_remote_amt) is int
to_remote = (bitcoin.TYPE_ADDRESS, remote_address, to_remote_amt // 1000) to_remote = (bitcoin.TYPE_ADDRESS, remote_address, to_remote_amt // 1000)
c_outputs = [to_local, to_remote] c_outputs = [to_local, to_remote]
for script, msat_amount in htlcs: for script, msat_amount in htlcs:

21
electrum/tests/test_lnhtlc.py

@ -74,12 +74,12 @@ def bip32(sequence):
assert type(k) is bytes assert type(k) is bytes
return k return k
def create_test_channels(): def create_test_channels(feerate=6000, local=None, remote=None):
funding_txid = binascii.hexlify(os.urandom(32)).decode("ascii") funding_txid = binascii.hexlify(os.urandom(32)).decode("ascii")
funding_index = 0 funding_index = 0
funding_sat = bitcoin.COIN * 10 funding_sat = ((local + remote) // 1000) if local is not None and remote is not None else (bitcoin.COIN * 10)
local_amount = (funding_sat * 1000) // 2 local_amount = local if local is not None else (funding_sat * 1000 // 2)
remote_amount = (funding_sat * 1000) // 2 remote_amount = remote if remote is not None else (funding_sat * 1000 // 2)
alice_raw = [ bip32("m/" + str(i)) for i in range(5) ] alice_raw = [ bip32("m/" + str(i)) for i in range(5) ]
bob_raw = [ bip32("m/" + str(i)) for i in range(5,11) ] bob_raw = [ bip32("m/" + str(i)) for i in range(5,11) ]
alice_privkeys = [lnbase.Keypair(lnbase.privkey_to_pubkey(x), x) for x in alice_raw] alice_privkeys = [lnbase.Keypair(lnbase.privkey_to_pubkey(x), x) for x in alice_raw]
@ -97,12 +97,21 @@ def create_test_channels():
return \ return \
lnhtlc.HTLCStateMachine( lnhtlc.HTLCStateMachine(
create_channel_state(funding_txid, funding_index, funding_sat, 6000, True, local_amount, remote_amount, alice_privkeys, bob_pubkeys, alice_seed, bob_cur, bob_next, b"\x02"*33, l_dust=200, r_dust=1300, l_csv=5, r_csv=4), "alice"), \ create_channel_state(funding_txid, funding_index, funding_sat, feerate, True, local_amount, remote_amount, alice_privkeys, bob_pubkeys, alice_seed, bob_cur, bob_next, b"\x02"*33, l_dust=200, r_dust=1300, l_csv=5, r_csv=4), "alice"), \
lnhtlc.HTLCStateMachine( lnhtlc.HTLCStateMachine(
create_channel_state(funding_txid, funding_index, funding_sat, 6000, False, remote_amount, local_amount, bob_privkeys, alice_pubkeys, bob_seed, alice_cur, alice_next, b"\x01"*33, l_dust=1300, r_dust=200, l_csv=4, r_csv=5), "bob") create_channel_state(funding_txid, funding_index, funding_sat, feerate, False, remote_amount, local_amount, bob_privkeys, alice_pubkeys, bob_seed, alice_cur, alice_next, b"\x01"*33, l_dust=1300, r_dust=200, l_csv=4, r_csv=5), "bob")
one_bitcoin_in_msat = bitcoin.COIN * 1000 one_bitcoin_in_msat = bitcoin.COIN * 1000
class TestFee(unittest.TestCase):
"""
test
https://github.com/lightningnetwork/lightning-rfc/blob/e0c436bd7a3ed6a028e1cb472908224658a14eca/03-transactions.md#requirements-2
"""
def test_SimpleAddSettleWorkflow(self):
alice_channel, bob_channel = create_test_channels(253, 10000000000, 5000000000)
self.assertIn(9999817, [x[2] for x in alice_channel.local_commitment.outputs()])
class TestLNBaseHTLCStateMachine(unittest.TestCase): class TestLNBaseHTLCStateMachine(unittest.TestCase):
def assertOutputExistsByValue(self, tx, amt_sat): def assertOutputExistsByValue(self, tx, amt_sat):
for typ, scr, val in tx.outputs(): for typ, scr, val in tx.outputs():

Loading…
Cancel
Save