Exemplo n.º 1
0
    def test_n_decoding(self):
        # We flip the signature recovery bit, which would normally give a different
        # pubkey.
        _, hrp, data = bech32_decode(lnencode(
            LnAddr(paymenthash=RHASH, amount=24, tags=[('d', '')]), PRIVKEY),
                                     ignore_long_length=True)
        databits = u5_to_bitarray(data)
        databits.invert(-1)
        lnaddr = lndecode(bech32_encode(segwit_addr.Encoding.BECH32, hrp,
                                        bitarray_to_u5(databits)),
                          verbose=True)
        assert lnaddr.pubkey.serialize() != PUBKEY

        # But not if we supply expliciy `n` specifier!
        _, hrp, data = bech32_decode(lnencode(
            LnAddr(paymenthash=RHASH,
                   amount=24,
                   tags=[('d', ''), ('n', PUBKEY)]), PRIVKEY),
                                     ignore_long_length=True)
        databits = u5_to_bitarray(data)
        databits.invert(-1)
        lnaddr = lndecode(bech32_encode(segwit_addr.Encoding.BECH32, hrp,
                                        bitarray_to_u5(databits)),
                          verbose=True)
        assert lnaddr.pubkey.serialize() == PUBKEY
Exemplo n.º 2
0
    def test_roundtrip(self):
        longdescription = (
            'One piece of chocolate cake, one icecream cone, one'
            ' pickle, one slice of swiss cheese, one slice of salami,'
            ' one lollypop, one piece of cherry pie, one sausage, one'
            ' cupcake, and one slice of watermelon')

        tests = [
            LnAddr(RHASH, tags=[('d', '')]),
            LnAddr(RHASH,
                   amount=Decimal('0.001'),
                   tags=[('d', '1 cup coffee'), ('x', 60)]),
            LnAddr(RHASH, amount=Decimal('1'), tags=[('h', longdescription)]),
            LnAddr(RHASH,
                   currency='tmona',
                   tags=[('f', 'mivTxWUqB6yxQdbLnAfcTSaVXouAhTUDfs'),
                         ('h', longdescription)]),
            LnAddr(
                RHASH,
                amount=24,
                tags=
                [('r',
                  [(unhexlify(
                      '029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'
                  ),
                    unhexlify('0102030405060708'), 1, 20, 3),
                   (unhexlify(
                       '039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'
                   ), unhexlify('030405060708090a'), 2, 30, 4)]),
                 ('f', 'MRHx4jW2KAQeEDMuK7pGLUGWvPRQT1Epmj'),
                 ('h', longdescription)]),
            LnAddr(RHASH,
                   amount=24,
                   tags=[('f', 'PHjTKtgYLTJ9D2Bzw2f6xBB41KBm2HeGfg'),
                         ('h', longdescription)]),
            LnAddr(RHASH,
                   amount=24,
                   tags=[('f', 'mona1quunc907zfyj7cyxhnp9584rj0wmdka2ec9w3af'),
                         ('h', longdescription)]),
            LnAddr(
                RHASH,
                amount=24,
                tags=
                [('f',
                  'mona1qp8f842ywwr9h5rdxyzggex7q3trvvvaarfssxccju52rj6htfzfsqr79j2'
                  ), ('h', longdescription)]),
            LnAddr(RHASH,
                   amount=24,
                   tags=[('n', PUBKEY), ('h', longdescription)]),
        ]

        # Roundtrip
        for t in tests:
            o = lndecode(lnencode(t, PRIVKEY), expected_hrp=t.currency)
            self.compare(t, o)
Exemplo n.º 3
0
 def test_min_final_cltv_expiry_roundtrip(self):
     for cltv in (1, 15, 16, 31, 32, 33, 150, 511, 512, 513, 1023, 1024,
                  1025):
         lnaddr = LnAddr(paymenthash=RHASH,
                         amount=Decimal('0.001'),
                         tags=[('d', '1 cup coffee'), ('x', 60),
                               ('c', cltv)])
         invoice = lnencode(lnaddr, PRIVKEY)
         self.assertEqual(cltv,
                          lndecode(invoice).get_min_final_cltv_expiry())
Exemplo n.º 4
0
    def test_n_decoding(self):
        # We flip the signature recovery bit, which would normally give a different
        # pubkey.
        hrp, data = bech32_decode(
            lnencode(LnAddr(RHASH, amount=24, tags=[('d', '')]), PRIVKEY),
            True)
        databits = u5_to_bitarray(data)
        databits.invert(-1)
        lnaddr = lndecode(bech32_encode(hrp, bitarray_to_u5(databits)), True)
        assert lnaddr.pubkey.serialize() != PUBKEY

        # But not if we supply expliciy `n` specifier!
        hrp, data = bech32_decode(
            lnencode(LnAddr(RHASH, amount=24, tags=[('d', ''), ('n', PUBKEY)]),
                     PRIVKEY), True)
        databits = u5_to_bitarray(data)
        databits.invert(-1)
        lnaddr = lndecode(bech32_encode(hrp, bitarray_to_u5(databits)), True)
        assert lnaddr.pubkey.serialize() == PUBKEY
Exemplo n.º 5
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(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 = channel_states.FUNDED
        bob_channel._state = channel_states.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(
                    RHASH,
                    amount_btc,
                    tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                          ('d', 'coffee')
                         ])
        return lnencode(lnaddr, w2.node_keypair.privkey)
Exemplo n.º 6
0
 def prepare_invoice(w2  # receiver
                     ):
     amount_btc = 100000 / Decimal(COIN)
     payment_preimage = os.urandom(32)
     RHASH = sha256(payment_preimage)
     addr = LnAddr(RHASH,
                   amount_btc,
                   tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                         ('d', 'coffee')])
     pay_req = lnencode(addr, w2.node_keypair.privkey)
     w2.preimages[bh2u(RHASH)] = bh2u(payment_preimage)
     w2.invoices[bh2u(RHASH)] = (pay_req, True, False)
     return pay_req
Exemplo n.º 7
0
 def prepare_invoice(w2  # receiver
                     ):
     amount_sat = 100000
     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(RHASH,
                     amount_btc,
                     tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                           ('d', 'coffee')])
     return lnencode(lnaddr, w2.node_keypair.privkey)
Exemplo n.º 8
0
        amount_sat=100_000,
        include_routing_hints=False,
    ):
        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)
        if include_routing_hints:
            routing_hints = await w2._calc_routing_hints_for_invoice(amount_sat
                                                                     )
        else:
            routing_hints = []
        lnaddr = LnAddr(paymenthash=RHASH,
                        amount=amount_btc,
                        tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                              ('d', 'coffee')] + routing_hints)
        return lnencode(lnaddr, w2.node_keypair.privkey)

    def test_reestablish(self):
        alice_channel, bob_channel = create_test_channels()
        p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel,
                                                      bob_channel)
        for chan in (alice_channel, bob_channel):
            chan.peer_state = PeerState.DISCONNECTED

        async def reestablish():
            await asyncio.gather(p1.reestablish_channel(alice_channel),
                                 p2.reestablish_channel(bob_channel))
            self.assertEqual(alice_channel.peer_state, PeerState.GOOD)
            self.assertEqual(bob_channel.peer_state, PeerState.GOOD)
Exemplo n.º 9
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()
        self._lnworkers_created = []  # type: List[MockLNWallet]

    def tearDown(self):
        async def cleanup_lnworkers():
            async with TaskGroup() as group:
                for lnworker in self._lnworkers_created:
                    await group.spawn(lnworker.stop())
            self._lnworkers_created.clear()
        run(cleanup_lnworkers())

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

    def prepare_peers(self, alice_channel, bob_channel):
        k1, k2 = keypair(), keypair()
        alice_channel.node_id = k2.pubkey
        bob_channel.node_id = k1.pubkey
        t1, t2 = transport_pair(k1, k2, alice_channel.name, bob_channel.name)
        q1, q2 = asyncio.Queue(), asyncio.Queue()
        w1 = MockLNWallet(local_keypair=k1, chans=[alice_channel], tx_queue=q1, name=bob_channel.name)
        w2 = MockLNWallet(local_keypair=k2, chans=[bob_channel], tx_queue=q2, name=alice_channel.name)
        self._lnworkers_created.extend([w1, w2])
        p1 = Peer(w1, k2.pubkey, t1)
        p2 = Peer(w2, k1.pubkey, t2)
        w1._peers[p1.pubkey] = p1
        w2._peers[p2.pubkey] = 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

    def prepare_chans_and_peers_in_square(self) -> SquareGraph:
        key_a, key_b, key_c, key_d = [keypair() for i in range(4)]
        chan_ab, chan_ba = create_test_channels(alice_name="alice", bob_name="bob", alice_pubkey=key_a.pubkey, bob_pubkey=key_b.pubkey)
        chan_ac, chan_ca = create_test_channels(alice_name="alice", bob_name="carol", alice_pubkey=key_a.pubkey, bob_pubkey=key_c.pubkey)
        chan_bd, chan_db = create_test_channels(alice_name="bob", bob_name="dave", alice_pubkey=key_b.pubkey, bob_pubkey=key_d.pubkey)
        chan_cd, chan_dc = create_test_channels(alice_name="carol", bob_name="dave", alice_pubkey=key_c.pubkey, bob_pubkey=key_d.pubkey)
        trans_ab, trans_ba = transport_pair(key_a, key_b, chan_ab.name, chan_ba.name)
        trans_ac, trans_ca = transport_pair(key_a, key_c, chan_ac.name, chan_ca.name)
        trans_bd, trans_db = transport_pair(key_b, key_d, chan_bd.name, chan_db.name)
        trans_cd, trans_dc = transport_pair(key_c, key_d, chan_cd.name, chan_dc.name)
        txq_a, txq_b, txq_c, txq_d = [asyncio.Queue() for i in range(4)]
        w_a = MockLNWallet(local_keypair=key_a, chans=[chan_ab, chan_ac], tx_queue=txq_a, name="alice")
        w_b = MockLNWallet(local_keypair=key_b, chans=[chan_ba, chan_bd], tx_queue=txq_b, name="bob")
        w_c = MockLNWallet(local_keypair=key_c, chans=[chan_ca, chan_cd], tx_queue=txq_c, name="carol")
        w_d = MockLNWallet(local_keypair=key_d, chans=[chan_db, chan_dc], tx_queue=txq_d, name="dave")
        self._lnworkers_created.extend([w_a, w_b, w_c, w_d])
        peer_ab = Peer(w_a, key_b.pubkey, trans_ab)
        peer_ac = Peer(w_a, key_c.pubkey, trans_ac)
        peer_ba = Peer(w_b, key_a.pubkey, trans_ba)
        peer_bd = Peer(w_b, key_d.pubkey, trans_bd)
        peer_ca = Peer(w_c, key_a.pubkey, trans_ca)
        peer_cd = Peer(w_c, key_d.pubkey, trans_cd)
        peer_db = Peer(w_d, key_b.pubkey, trans_db)
        peer_dc = Peer(w_d, key_c.pubkey, trans_dc)
        w_a._peers[peer_ab.pubkey] = peer_ab
        w_a._peers[peer_ac.pubkey] = peer_ac
        w_b._peers[peer_ba.pubkey] = peer_ba
        w_b._peers[peer_bd.pubkey] = peer_bd
        w_c._peers[peer_ca.pubkey] = peer_ca
        w_c._peers[peer_cd.pubkey] = peer_cd
        w_d._peers[peer_db.pubkey] = peer_db
        w_d._peers[peer_dc.pubkey] = peer_dc

        w_b.network.config.set_key('lightning_forward_payments', True)
        w_c.network.config.set_key('lightning_forward_payments', True)

        # forwarding fees, etc
        chan_ab.forwarding_fee_proportional_millionths *= 500
        chan_ab.forwarding_fee_base_msat *= 500
        chan_ba.forwarding_fee_proportional_millionths *= 500
        chan_ba.forwarding_fee_base_msat *= 500
        chan_bd.forwarding_fee_proportional_millionths *= 500
        chan_bd.forwarding_fee_base_msat *= 500
        chan_db.forwarding_fee_proportional_millionths *= 500
        chan_db.forwarding_fee_base_msat *= 500

        # mark_open won't work if state is already OPEN.
        # so set it to FUNDED
        for chan in [chan_ab, chan_ac, chan_ba, chan_bd, chan_ca, chan_cd, chan_db, chan_dc]:
            chan._state = ChannelState.FUNDED
        # this populates the channel graph:
        peer_ab.mark_open(chan_ab)
        peer_ac.mark_open(chan_ac)
        peer_ba.mark_open(chan_ba)
        peer_bd.mark_open(chan_bd)
        peer_ca.mark_open(chan_ca)
        peer_cd.mark_open(chan_cd)
        peer_db.mark_open(chan_db)
        peer_dc.mark_open(chan_dc)
        return SquareGraph(
            w_a=w_a,
            w_b=w_b,
            w_c=w_c,
            w_d=w_d,
            peer_ab=peer_ab,
            peer_ac=peer_ac,
            peer_ba=peer_ba,
            peer_bd=peer_bd,
            peer_ca=peer_ca,
            peer_cd=peer_cd,
            peer_db=peer_db,
            peer_dc=peer_dc,
            chan_ab=chan_ab,
            chan_ac=chan_ac,
            chan_ba=chan_ba,
            chan_bd=chan_bd,
            chan_ca=chan_ca,
            chan_cd=chan_cd,
            chan_db=chan_db,
            chan_dc=chan_dc,
        )

    @staticmethod
    async def prepare_invoice(
            w2: MockLNWallet,  # receiver
            *,
            amount_msat=100_000_000,
            include_routing_hints=False,
    ) -> Tuple[LnAddr, str]:
        amount_btc = amount_msat/Decimal(COIN*1000)
        payment_preimage = os.urandom(32)
        RHASH = sha256(payment_preimage)
        info = PaymentInfo(RHASH, amount_msat, RECEIVED, PR_UNPAID)
        w2.save_preimage(RHASH, payment_preimage)
        w2.save_payment_info(info)
        if include_routing_hints:
            routing_hints = await w2._calc_routing_hints_for_invoice(amount_msat)
        else:
            routing_hints = []
        trampoline_hints = []
        for r in routing_hints:
            node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
            if len(r[1])== 1 and w2.is_trampoline_peer(node_id):
                trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
        invoice_features = w2.features.for_invoice()
        if invoice_features.supports(LnFeatures.PAYMENT_SECRET_OPT):
            payment_secret = derive_payment_secret_from_payment_preimage(payment_preimage)
        else:
            payment_secret = None
        lnaddr1 = LnAddr(
                    paymenthash=RHASH,
                    amount=amount_btc,
                    tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                          ('d', 'coffee'),
                          ('9', invoice_features),
                         ] + routing_hints + trampoline_hints,
                    payment_secret=payment_secret,
        )
        invoice = lnencode(lnaddr1, w2.node_keypair.privkey)
        lnaddr2 = lndecode(invoice)  # unlike lnaddr1, this now has a pubkey set
        return lnaddr2, invoice
Exemplo n.º 10
0
 def test_min_final_cltv_expiry_roundtrip(self):
     lnaddr = LnAddr(RHASH, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60), ('c', 150)])
     invoice = lnencode(lnaddr, PRIVKEY)
     self.assertEqual(150, lndecode(invoice).get_min_final_cltv_expiry())
Exemplo n.º 11
0
    def test_roundtrip(self):
        longdescription = (
            'One piece of chocolate cake, one icecream cone, one'
            ' pickle, one slice of swiss cheese, one slice of salami,'
            ' one lollypop, one piece of cherry pie, one sausage, one'
            ' cupcake, and one slice of watermelon')

        timestamp = 1615922274
        tests = [
            (LnAddr(date=timestamp, paymenthash=RHASH, tags=[('d', '')]),
             "lnmona1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqyhhfge73xtqkqr3ca22rvu3hyxfmesxahwyxdh3tya02p7x7ah3qt6zz4s5yn25z6hh4urf8c3598sdl8zxjpph0fg35y0kfn8vq6vsqd340h9"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=Decimal('0.001'),
                    tags=[('d', '1 cup coffee'), ('x', 60)]),
             "lnmona1m1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu206axye9c7fz0sc6y6f2e97l48mgpxv3r6v5k8rt7ce082fl64vskpcxvypnmjeg5amxe79sylp900rxgv2k8el535c0y22807v4ggsqglal97"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=Decimal('1'),
                    tags=[('h', longdescription)]),
             "lnmona11ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs2hgyzzwfazhwxpj3edw9rw3gmsmcl3ktrzdeh3nkldnhzc0g6gfpkg3sqzx2dhuzetqsq6weypkegn3sfcg2tqyv0eyf6u7yq8rqw3gp764v5x"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    net=constants.BitcoinTestnet,
                    tags=[('f', 'mivTxWUqB6yxQdbLnAfcTSaVXouAhTUDfs'),
                          ('h', longdescription)]),
             "lntmona1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3y4dtfd4mcpuul3wmteaxatgldveymxxyhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsq06ue05mjfwgnra27g6tnhzywtj7qh5rhuutf77apa7dclddp4nz0zwjcvqm4j8fd2pm2u8rwultx232yvdxsmh5fueffjexfuzn40gpaz4tq5"
             ),
            (LnAddr(
                date=timestamp,
                paymenthash=RHASH,
                amount=24,
                tags=
                [('r',
                  [(unhexlify(
                      '029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'
                  ), unhexlify('0102030405060708'), 1, 20, 3),
                   (unhexlify(
                       '039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'
                   ), unhexlify('030405060708090a'), 2, 30, 4)]),
                 ('f', 'MRHx4jW2KAQeEDMuK7pGLUGWvPRQT1Epmj'),
                 ('h', longdescription)]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqfpp3hmyccldvzvekns7z4cmvu0lsg7stk9r2hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs0m55wzqx4wrwhx27e9q830ua7gf0q8ft8axr6td50ztezsh84ra4ql6za2yk8462x4c7n3agqvtc6rxaug7f8udpv7faq0czepz3q0gp5zm5qk"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('f', 'PHjTKtgYLTJ9D2Bzw2f6xBB41KBm2HeGfg'),
                          ('h', longdescription)]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppjv3yl26xfe53hsyu0ycmwz4n3z2scf20ghp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs94ex52vmwdkq0rsxjr8r7k8k40egy745aqlws0yk7j793zv5tqfq4arqjlw7peu8r62hvpkcqjquuztgst47vpzqzt8ju8c36gqx2tgp3jmyzn"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('f',
                           'mona1quunc907zfyj7cyxhnp9584rj0wmdka2ec9w3af'),
                          ('h', longdescription)]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppquunc907zfyj7cyxhnp9584rj0wmdka2ehp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsqvn67ssxdtqakqvxfy4lsen8sqw4pzd2j230lz7dx6chd737csyjcfe93dc4s099faaeg37us2dzt0pwfrtfnp429k40vfu06qpf28squq5wy0"
             ),
            (LnAddr(
                date=timestamp,
                paymenthash=RHASH,
                amount=24,
                tags=
                [('f',
                  'mona1qp8f842ywwr9h5rdxyzggex7q3trvvvaarfssxccju52rj6htfzfsqr79j2'
                  ), ('h', longdescription)]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qp8f842ywwr9h5rdxyzggex7q3trvvvaarfssxccju52rj6htfzfshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsd45jh4k2e8jvhzpthqyc2sspr30k3pg67f4a973qumvw48t4gzl4tdr9qh7p04z5afghspsapvp3tahcq7rasw7dtv2vv46sm79479qpm9jdjl"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('n', PUBKEY), ('h', longdescription)]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsdcmmg0p9qn003ls0r8dv2f9d7xh7lfx6lsha7h5azxzfdd292r4yuflmhs3dkkghmzaecedwzrvxktsnrhcfmsnx7r64chxw5phrwggq32hqsz"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription), ('9', 514)]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qzszz2jjnvrh9rg877px70r5cfrf0vqwzws0jxmcg3d4pqg0ml337s8qsmxjyedvssge254qjwucnjy3rtzr7gxzfatsy3ad5x4c0ll93acq3rwstg"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription), ('9', 10 + (1 << 8))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qzg2vwag8ax9f9wkmsz37nvq8qkq53tpznev8jjnkezq7u8qzzytad85zhxgk7hqgqkqrftazq8hfr6lv9q4j4vk2ld6ymrjtsnumetlhjqqe7cl7z"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription), ('9', 10 + (1 << 9))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qzs2p8uyuepjhdpyfsv6xuc0enx57upz4sdz0n38s7mfc07lpm5vvc9zdursykxpz8heddckdg2d6wsjltuf93rrewptuekp5s9w06wucacqgjaara"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription),
                          ('9', 10 + (1 << 7) + (1 << 11))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrzy2xw96qw57wvauu8skgsvlpnvqluwy4txpm7pw3q9r70lryezhvjrjj9f7tuage9une2hn26quv9fpm9wa4h672288m5ek4jk4gh2cm5sqlkg3yn"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription), ('9', 10 + (1 << 12))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qryq29tefcvke7zl35fjehvn0t3vnw2uae766g7gxuj23tk4vfsxjzk2n45vnms6ew2pvydlhy7vfxptnwcs0x7qjuct0feg4vryzl3trvlspfvn5v7"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription), ('9', 10 + (1 << 13))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrgq2yctvzkmsa8w0elyd7gx5wp5lflsx8wsqw5l2ejytgd3q5qrwjkckvqsjamwq93eekje8qwzphsjh9hcy3lmpvrdlks8tmutuad3k9eqquhzhfz"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription),
                          ('9', 10 + (1 << 9) + (1 << 14))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrss2dccexrncjpgvjfy93t507h07l63qt7k7fp3zeptdqadv9jmulu6hfa6syjwqqrvjf3a9fu50ap0vtjtrmyrqzjg46are0apf6hq6ztqqfhkr7e"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription),
                          ('9', 10 + (1 << 9) + (1 << 15))]),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqs29trq3u6ctz9qnm4hate5u74f0w6j4tcjj3wr79czn4n48egmze98kwk8wt6e6cj5890jpd8dvwfttua2qullslfyf04save5fnvgp9sqkusukq"
             ),
            (LnAddr(date=timestamp,
                    paymenthash=RHASH,
                    amount=24,
                    tags=[('h', longdescription), ('9', 33282)],
                    payment_secret=b"\x11" * 32),
             "lnmona241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqsz29tu4q7pmhu58qywk2mfd5vpuscy308del7mq76pywkqtjz36f240cgcxrpuzzzzprw7gvnwvwn6vxnpm7dtg2t258f0zy0zl7ldnrcpvzyrkz"
             ),
        ]

        # Roundtrip
        for lnaddr1, invoice_str1 in tests:
            invoice_str2 = lnencode(lnaddr1, PRIVKEY)
            self.assertEqual(invoice_str1, invoice_str2)
            lnaddr2 = lndecode(invoice_str2, net=lnaddr1.net)
            self.compare(lnaddr1, lnaddr2)