URI: 
       tlnchannel: test for max htlc value (needs to be below protocol maximum) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 53c6fc8cf14a914e961bd2f137540684244b461c
   DIR parent 777e350fae40afadb367914b7127a6ebe3f0241b
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Thu, 26 Mar 2020 06:25:26 +0100
       
       lnchannel: test for max htlc value (needs to be below protocol maximum)
       
       Diffstat:
         M electrum/lnchannel.py               |      18 +++++++++++-------
         M electrum/lnutil.py                  |       1 +
         M electrum/tests/regtest/regtest.sh   |       8 ++++----
         M electrum/tests/test_lnchannel.py    |      40 +++++++++++++++++++++++++++----
       
       4 files changed, 51 insertions(+), 16 deletions(-)
       ---
   DIR diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
       t@@ -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
       t@@ -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
       t@@ -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.
   DIR diff --git a/electrum/lnutil.py b/electrum/lnutil.py
       t@@ -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():
   DIR diff --git a/electrum/tests/regtest/regtest.sh b/electrum/tests/regtest/regtest.sh
       t@@ -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
       t@@ -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
       t@@ -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')
       t@@ -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
        
   DIR diff --git a/electrum/tests/test_lnchannel.py b/electrum/tests/test_lnchannel.py
       t@@ -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]
       t@@ -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):
       t@@ -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):
       t@@ -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()