Browse Source

lnchannel: test for max htlc value (needs to be below protocol maximum)

hard-fail-on-bad-server-string
SomberNight 5 years ago
parent
commit
53c6fc8cf1
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 18
      electrum/lnchannel.py
  2. 1
      electrum/lnutil.py
  3. 8
      electrum/tests/regtest/regtest.sh
  4. 40
      electrum/tests/test_lnchannel.py

18
electrum/lnchannel.py

@ -44,13 +44,14 @@ from .logging import Logger
from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailureMessage
from . import lnutil
from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, ChannelConstraints,
get_per_commitment_secret_from_seed, secret_to_pubkey, derive_privkey, make_closing_tx,
sign_and_get_sig_string, RevocationStore, derive_blinded_pubkey, Direction, derive_pubkey,
make_htlc_tx_with_open_channel, make_commitment, make_received_htlc, make_offered_htlc,
HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT, extract_ctn_from_tx_and_chan, UpdateAddHtlc,
funding_output_script, SENT, RECEIVED, LOCAL, REMOTE, HTLCOwner, make_commitment_outputs,
ScriptHtlc, PaymentFailure, calc_onchain_fees, RemoteMisbehaving, make_htlc_output_witness_script,
ShortChannelID, map_htlcs_to_ctx_output_idxs, LNPeerAddr, BarePaymentAttemptLog)
get_per_commitment_secret_from_seed, secret_to_pubkey, derive_privkey, make_closing_tx,
sign_and_get_sig_string, RevocationStore, derive_blinded_pubkey, Direction, derive_pubkey,
make_htlc_tx_with_open_channel, make_commitment, make_received_htlc, make_offered_htlc,
HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT, extract_ctn_from_tx_and_chan, UpdateAddHtlc,
funding_output_script, SENT, RECEIVED, LOCAL, REMOTE, HTLCOwner, make_commitment_outputs,
ScriptHtlc, PaymentFailure, calc_onchain_fees, RemoteMisbehaving, make_htlc_output_witness_script,
ShortChannelID, map_htlcs_to_ctx_output_idxs, LNPeerAddr, BarePaymentAttemptLog,
LN_MAX_HTLC_VALUE_MSAT)
from .lnsweep import create_sweeptxs_for_our_ctx, create_sweeptxs_for_their_ctx
from .lnsweep import create_sweeptx_for_their_revoked_htlc, SweepInfo
from .lnhtlc import HTLCManager
@ -159,6 +160,7 @@ class Channel(Logger):
self.revocation_store = RevocationStore(state["revocation_store"])
self._can_send_ctx_updates = True # type: bool
self._receive_fail_reasons = {} # type: Dict[int, BarePaymentAttemptLog]
self._ignore_max_htlc_value = False # used in tests
def get_id_for_log(self) -> str:
scid = self.short_channel_id
@ -425,6 +427,8 @@ class Channel(Logger):
raise PaymentFailure(f'HTLC value sum (sum of pending htlcs: {current_htlc_sum/1000} sat plus new htlc: {amount_msat/1000} sat) would exceed max allowed: {self.config[REMOTE].max_htlc_value_in_flight_msat/1000} sat')
if amount_msat < self.config[REMOTE].htlc_minimum_msat:
raise PaymentFailure(f'HTLC value too small: {amount_msat} msat')
if amount_msat > LN_MAX_HTLC_VALUE_MSAT and not self._ignore_max_htlc_value:
raise PaymentFailure(f"HTLC value over protocol maximum: {amount_msat} > {LN_MAX_HTLC_VALUE_MSAT} msat")
def can_pay(self, amount_msat: int) -> bool:
"""Returns whether we can initiate a new payment of given value.

1
electrum/lnutil.py

@ -34,6 +34,7 @@ HTLC_TIMEOUT_WEIGHT = 663
HTLC_SUCCESS_WEIGHT = 703
LN_MAX_FUNDING_SAT = pow(2, 24) - 1
LN_MAX_HTLC_VALUE_MSAT = pow(2, 32) - 1
# dummy address for fee estimation of funding tx
def ln_dummy_address():

8
electrum/tests/regtest/regtest.sh

@ -171,7 +171,7 @@ if [[ $1 == "redeem_htlcs" ]]; then
new_blocks 3
wait_until_channel_open alice
# alice pays bob
invoice=$($bob add_lightning_request 0.05 -m "test")
invoice=$($bob add_lightning_request 0.04 -m "test")
$alice lnpay $invoice --timeout=1 || true
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
if [[ "$unsettled" == "0" ]]; then
@ -213,7 +213,7 @@ if [[ $1 == "breach_with_unspent_htlc" ]]; then
new_blocks 3
wait_until_channel_open alice
echo "alice pays bob"
invoice=$($bob add_lightning_request 0.05 -m "test")
invoice=$($bob add_lightning_request 0.04 -m "test")
$alice lnpay $invoice --timeout=1 || true
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
if [[ "$unsettled" == "0" ]]; then
@ -242,7 +242,7 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
new_blocks 3
wait_until_channel_open alice
echo "alice pays bob"
invoice=$($bob add_lightning_request 0.05 -m "test")
invoice=$($bob add_lightning_request 0.04 -m "test")
$alice lnpay $invoice --timeout=1 || true
ctx=$($alice get_channel_ctx $channel --iknowwhatimdoing)
unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent')
@ -284,7 +284,7 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
$bob daemon -d
sleep 1
$bob load_wallet
wait_for_balance bob 0.049
wait_for_balance bob 0.039
$bob getbalance
fi

40
electrum/tests/test_lnchannel.py

@ -105,12 +105,12 @@ def bip32(sequence):
assert type(k) is bytes
return k
def create_test_channels(feerate=6000, local=None, remote=None):
def create_test_channels(*, feerate=6000, local_msat=None, remote_msat=None):
funding_txid = binascii.hexlify(b"\x01"*32).decode("ascii")
funding_index = 0
funding_sat = ((local + remote) // 1000) if local is not None and remote is not None else (bitcoin.COIN * 10)
local_amount = local if local is not None else (funding_sat * 1000 // 2)
remote_amount = remote if remote is not None else (funding_sat * 1000 // 2)
funding_sat = ((local_msat + remote_msat) // 1000) if local_msat is not None and remote_msat is not None else (bitcoin.COIN * 10)
local_amount = local_msat if local_msat is not None else (funding_sat * 1000 // 2)
remote_amount = remote_msat if remote_msat is not None else (funding_sat * 1000 // 2)
alice_raw = [ bip32("m/" + str(i)) for i in range(5) ]
bob_raw = [ bip32("m/" + str(i)) for i in range(5,11) ]
alice_privkeys = [lnutil.Keypair(lnutil.privkey_to_pubkey(x), x) for x in alice_raw]
@ -164,6 +164,10 @@ def create_test_channels(feerate=6000, local=None, remote=None):
# TODO: sweep_address in lnchannel.py should use static_remotekey
alice.sweep_address = bitcoin.pubkey_to_address('p2wpkh', alice.config[LOCAL].payment_basepoint.pubkey.hex())
bob.sweep_address = bitcoin.pubkey_to_address('p2wpkh', bob.config[LOCAL].payment_basepoint.pubkey.hex())
alice._ignore_max_htlc_value = True
bob._ignore_max_htlc_value = True
return alice, bob
class TestFee(ElectrumTestCase):
@ -172,7 +176,9 @@ class TestFee(ElectrumTestCase):
https://github.com/lightningnetwork/lightning-rfc/blob/e0c436bd7a3ed6a028e1cb472908224658a14eca/03-transactions.md#requirements-2
"""
def test_fee(self):
alice_channel, bob_channel = create_test_channels(253, 10000000000, 5000000000)
alice_channel, bob_channel = create_test_channels(feerate=253,
local_msat=10000000000,
remote_msat=5000000000)
self.assertIn(9999817, [x.value for x in alice_channel.get_latest_commitment(LOCAL).outputs()])
class TestChannel(ElectrumTestCase):
@ -649,6 +655,30 @@ class TestAvailableToSpend(ElectrumTestCase):
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
alice_channel.add_htlc(htlc_dict)
def test_max_htlc_value(self):
alice_channel, bob_channel = create_test_channels()
paymentPreimage = b"\x01" * 32
paymentHash = bitcoin.sha256(paymentPreimage)
htlc_dict = {
'payment_hash' : paymentHash,
'amount_msat' : one_bitcoin_in_msat * 41 // 10,
'cltv_expiry' : 5,
'timestamp' : 0,
}
alice_channel._ignore_max_htlc_value = False
bob_channel._ignore_max_htlc_value = False
with self.assertRaises(lnutil.PaymentFailure):
alice_channel.add_htlc(htlc_dict)
with self.assertRaises(lnutil.PaymentFailure):
bob_channel.receive_htlc(htlc_dict)
alice_channel._ignore_max_htlc_value = True
bob_channel._ignore_max_htlc_value = True
alice_channel.add_htlc(htlc_dict)
bob_channel.receive_htlc(htlc_dict)
class TestChanReserve(ElectrumTestCase):
def setUp(self):
alice_channel, bob_channel = create_test_channels()

Loading…
Cancel
Save