Beispiel #1
0
 def part2(self):
     paymentPreimage = b"\x01" * 32
     paymentHash = bitcoin.sha256(paymentPreimage)
     # Now we'll add HTLC of 3.5 BTC to Alice's commitment, this should put
     # Alice's balance at 1.5 BTC.
     #
     # Resulting balances:
     #	Alice:	1.5
     #	Bob:	9.5
     htlc_dict = {
         'payment_hash': paymentHash,
         'amount_msat': int(3.5 * one_bitcoin_in_msat),
         'cltv_expiry': 5,
     }
     self.alice_channel.add_htlc(htlc_dict)
     self.bob_channel.receive_htlc(htlc_dict)
     # Add a second HTLC of 1 BTC. This should fail because it will take
     # Alice's balance all the way down to her channel reserve, but since
     # she is the initiator the additional transaction fee makes her
     # balance dip below.
     htlc_dict['amount_msat'] = one_bitcoin_in_msat
     with self.assertRaises(lnutil.PaymentFailure):
         self.alice_channel.add_htlc(htlc_dict)
     with self.assertRaises(lnutil.RemoteMisbehaving):
         self.bob_channel.receive_htlc(htlc_dict)
Beispiel #2
0
 def part3(self):
     # Add a HTLC of 2 BTC to Alice, and the settle it.
     # Resulting balances:
     #	Alice:	3.0
     #	Bob:	7.0
     paymentPreimage = b"\x01" * 32
     paymentHash = bitcoin.sha256(paymentPreimage)
     htlc_dict = {
         'payment_hash': paymentHash,
         'amount_msat': int(2 * one_bitcoin_in_msat),
         'cltv_expiry': 5,
         'timestamp': 0,
     }
     alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id
     bob_idx = self.bob_channel.receive_htlc(htlc_dict).htlc_id
     force_state_transition(self.alice_channel, self.bob_channel)
     self.check_bals(
         one_bitcoin_in_msat * 3 - self.alice_channel.get_next_fee(LOCAL),
         one_bitcoin_in_msat * 5)
     self.bob_channel.settle_htlc(paymentPreimage, bob_idx)
     self.alice_channel.receive_htlc_settle(paymentPreimage, alice_idx)
     force_state_transition(self.alice_channel, self.bob_channel)
     self.check_bals(
         one_bitcoin_in_msat * 3 - self.alice_channel.get_next_fee(LOCAL),
         one_bitcoin_in_msat * 7)
     # And now let Bob add an HTLC of 1 BTC. This will take Bob's balance
     # all the way down to his channel reserve, but since he is not paying
     # the fee this is okay.
     htlc_dict['amount_msat'] = one_bitcoin_in_msat
     self.bob_channel.add_htlc(htlc_dict)
     self.alice_channel.receive_htlc(htlc_dict)
     force_state_transition(self.alice_channel, self.bob_channel)
     self.check_bals(one_bitcoin_in_msat * 3 \
                     - self.alice_channel.get_next_fee(LOCAL),
                     one_bitcoin_in_msat * 6)
Beispiel #3
0
    def setUp(self):
        super().setUp()
        # Create a test channel which will be used for the duration of this
        # unittest. The channel will be funded evenly with Alice having 5 BTC,
        # and Bob having 5 BTC.
        self.alice_channel, self.bob_channel = create_test_channels()

        self.paymentPreimage = b"\x01" * 32
        paymentHash = bitcoin.sha256(self.paymentPreimage)
        self.htlc_dict = {
            'payment_hash': paymentHash,
            'amount_msat': one_bitcoin_in_msat,
            'cltv_expiry': 5,
            'timestamp': 0,
        }

        # First Alice adds the outgoing HTLC to her local channel's state
        # update log. Then Alice sends this wire message over to Bob who adds
        # this htlc to his remote state update log.
        self.aliceHtlcIndex = self.alice_channel.add_htlc(
            self.htlc_dict).htlc_id
        self.assertNotEqual(
            list(
                self.alice_channel.hm.htlcs_by_direction(REMOTE, RECEIVED,
                                                         1).values()), [])

        before = self.bob_channel.balance_minus_outgoing_htlcs(REMOTE)
        beforeLocal = self.bob_channel.balance_minus_outgoing_htlcs(LOCAL)

        self.bobHtlcIndex = self.bob_channel.receive_htlc(
            self.htlc_dict).htlc_id

        self.htlc = self.bob_channel.hm.log[REMOTE]['adds'][0]
class TestPeer(ElectrumTestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        console_stderr_handler.setLevel(logging.DEBUG)

    def setUp(self):
        super().setUp()
        self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop(
        )

    def tearDown(self):
        super().tearDown()
        self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1)
        self._loop_thread.join(timeout=1)

    def prepare_peers(self, alice_channel, bob_channel):
        k1, k2 = keypair(), keypair()
        t1, t2 = transport_pair(k2, k1, alice_channel.name, bob_channel.name)
        q1, q2 = asyncio.Queue(), asyncio.Queue()
        w1 = MockLNWallet(k1, k2, alice_channel, tx_queue=q1)
        w2 = MockLNWallet(k2, k1, bob_channel, tx_queue=q2)
        p1 = Peer(w1, k1.pubkey, t1)
        p2 = Peer(w2, k2.pubkey, t2)
        w1.peer = p1
        w2.peer = p2
        # mark_open won't work if state is already OPEN.
        # so set it to FUNDED
        alice_channel._state = ChannelState.FUNDED
        bob_channel._state = ChannelState.FUNDED
        # this populates the channel graph:
        p1.mark_open(alice_channel)
        p2.mark_open(bob_channel)
        return p1, p2, w1, w2, q1, q2

    @staticmethod
    def prepare_invoice(
        w2,  # receiver
        *,
        amount_sat=100_000,
    ):
        amount_btc = amount_sat / Decimal(COIN)
        payment_preimage = os.urandom(32)
        RHASH = sha256(payment_preimage)
        info = PaymentInfo(RHASH, amount_sat, RECEIVED, PR_UNPAID)
        w2.save_preimage(RHASH, payment_preimage)
        w2.save_payment_info(info)
        lnaddr = LnAddr(paymenthash=RHASH,
                        amount=amount_btc,
                        tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                              ('d', 'coffee')])
        return lnencode(lnaddr, w2.node_keypair.privkey)
Beispiel #5
0
    def test_part1(self):
        # Add an HTLC that will increase Bob's balance. This should succeed,
        # since Alice stays above her channel reserve, and Bob increases his
        # balance (while still being below his channel reserve).
        #
        # Resulting balances:
        #	Alice:	4.5
        #	Bob:	5.0
        paymentPreimage = b"\x01" * 32
        paymentHash = bitcoin.sha256(paymentPreimage)
        htlc_dict = {
            'payment_hash': paymentHash,
            'amount_msat': int(.5 * one_bitcoin_in_msat),
            'cltv_expiry': 5,
            'timestamp': 0,
        }
        self.alice_channel.add_htlc(htlc_dict)
        self.bob_channel.receive_htlc(htlc_dict)
        # Force a state transition, making sure this HTLC is considered valid
        # even though the channel reserves are not met.
        force_state_transition(self.alice_channel, self.bob_channel)

        aliceSelfBalance = self.alice_channel.balance(LOCAL)\
                - lnchannel.htlcsum(self.alice_channel.hm.htlcs_by_direction(LOCAL, SENT).values())
        bobBalance = self.bob_channel.balance(REMOTE)\
                - lnchannel.htlcsum(self.alice_channel.hm.htlcs_by_direction(REMOTE, SENT).values())
        self.assertEqual(aliceSelfBalance, one_bitcoin_in_msat * 4.5)
        self.assertEqual(bobBalance, one_bitcoin_in_msat * 5)
        # Now let Bob try to add an HTLC. This should fail, since it will
        # decrease his balance, which is already below the channel reserve.
        #
        # Resulting balances:
        #	Alice:	4.5
        #	Bob:	5.0
        with self.assertRaises(lnutil.PaymentFailure):
            htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02')
            self.bob_channel.add_htlc(htlc_dict)
        with self.assertRaises(lnutil.RemoteMisbehaving):
            self.alice_channel.receive_htlc(htlc_dict)
Beispiel #6
0
    def test_AddHTLCNegativeBalance(self):
        # the test in lnd doesn't set the fee to zero.
        # probably lnd subtracts commitment fee after deciding weather
        # an htlc can be added. so we set the fee to zero so that
        # the test can work.
        self.alice_to_bob_fee_update(0)
        force_state_transition(self.alice_channel, self.bob_channel)

        self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02')
        self.alice_channel.add_htlc(self.htlc_dict)
        self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x03')
        self.alice_channel.add_htlc(self.htlc_dict)
        # now there are three htlcs (one was in setUp)

        # Alice now has an available balance of 2 BTC. We'll add a new HTLC of
        # value 2 BTC, which should make Alice's balance negative (since she
        # has to pay a commitment fee).
        new = dict(self.htlc_dict)
        new['amount_msat'] *= 2.5
        new['payment_hash'] = bitcoin.sha256(32 * b'\x04')
        with self.assertRaises(lnutil.PaymentFailure) as cm:
            self.alice_channel.add_htlc(new)
        self.assertIn('Not enough local balance', cm.exception.args[0])
Beispiel #7
0
    def test_DesyncHTLCs(self):
        alice_channel, bob_channel = create_test_channels()
        self.assertEqual(499994624000, alice_channel.available_to_spend(LOCAL))
        self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))

        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_idx = alice_channel.add_htlc(htlc_dict).htlc_id
        bob_idx = bob_channel.receive_htlc(htlc_dict).htlc_id
        self.assertEqual(89993592000, alice_channel.available_to_spend(LOCAL))
        self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))

        force_state_transition(alice_channel, bob_channel)
        bob_channel.fail_htlc(bob_idx)
        alice_channel.receive_fail_htlc(alice_idx, error_bytes=None)
        self.assertEqual(89993592000, alice_channel.available_to_spend(LOCAL))
        self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
        # Alice now has gotten all her original balance (5 BTC) back, however,
        # adding a new HTLC at this point SHOULD fail, since if she adds the
        # HTLC and signs the next state, Bob cannot assume she received the
        # FailHTLC, and must assume she doesn't have the necessary balance
        # available.
        # We try adding an HTLC of value 1 BTC, which should fail because the
        # balance is unavailable.
        htlc_dict = {
            'payment_hash': paymentHash,
            'amount_msat': one_bitcoin_in_msat,
            'cltv_expiry': 5,
            'timestamp': 0,
        }
        with self.assertRaises(lnutil.PaymentFailure):
            alice_channel.add_htlc(htlc_dict)
        # Now do a state transition, which will ACK the FailHTLC, making Alice
        # able to add the new HTLC.
        force_state_transition(alice_channel, bob_channel)
        self.assertEqual(499994624000, alice_channel.available_to_spend(LOCAL))
        self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
        alice_channel.add_htlc(htlc_dict)
Beispiel #8
0
    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.RemoteMisbehaving):
            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)
Beispiel #9
0
    def test_DustLimit(self):
        alice_channel, bob_channel = create_test_channels()

        paymentPreimage = b"\x01" * 32
        paymentHash = bitcoin.sha256(paymentPreimage)
        fee_per_kw = alice_channel.get_next_feerate(LOCAL)
        self.assertEqual(fee_per_kw, 6000)
        htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000)
        self.assertEqual(htlcAmt, 4478)
        htlc = {
            'payment_hash': paymentHash,
            'amount_msat': 1000 * htlcAmt,
            'cltv_expiry': 5,  # also in create_test_channels
            'timestamp': 0,
        }

        old_values = [
            x.value
            for x in bob_channel.get_latest_commitment(LOCAL).outputs()
        ]
        aliceHtlcIndex = alice_channel.add_htlc(htlc).htlc_id
        bobHtlcIndex = bob_channel.receive_htlc(htlc).htlc_id
        force_state_transition(alice_channel, bob_channel)
        alice_ctx = alice_channel.get_latest_commitment(LOCAL)
        bob_ctx = bob_channel.get_latest_commitment(LOCAL)
        new_values = [x.value for x in bob_ctx.outputs()]
        self.assertNotEqual(old_values, new_values)
        self.assertEqual(len(alice_ctx.outputs()), 3)
        self.assertEqual(len(bob_ctx.outputs()), 2)
        default_fee = calc_static_fee(0)
        self.assertEqual(bob_channel.get_next_fee(LOCAL),
                         default_fee + htlcAmt)
        bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex)
        alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex)
        force_state_transition(bob_channel, alice_channel)
        self.assertEqual(
            len(alice_channel.get_next_commitment(LOCAL).outputs()), 2)
        self.assertEqual(alice_channel.total_msat(SENT) // 1000, htlcAmt)
Beispiel #10
0
    def test_concurrent_reversed_payment(self):
        self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02')
        self.htlc_dict['amount_msat'] += 1000
        self.bob_channel.add_htlc(self.htlc_dict)
        self.alice_channel.receive_htlc(self.htlc_dict)

        self.assertEqual(
            len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
        self.assertEqual(
            len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
        self.assertEqual(
            len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
        self.assertEqual(
            len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3)

        self.alice_channel.receive_new_commitment(
            *self.bob_channel.sign_next_commitment())

        self.assertEqual(
            len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3)
        self.assertEqual(
            len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
        self.assertEqual(
            len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
        self.assertEqual(
            len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3)

        self.alice_channel.revoke_current_commitment()

        self.assertEqual(
            len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3)
        self.assertEqual(
            len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
        self.assertEqual(
            len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
        self.assertEqual(
            len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 4)