Example #1
0
def test_open_channel_bad_type(runner: Runner, with_proposal: Any) -> None:
    """Tests for https://github.com/lightningnetwork/lightning-rfc/pull/880"""
    with_proposal(channel_type_csv)

    # This is not a feature bit, so use support_ to mark it.
    if runner.has_option('supports_open_accept_channel_types') is None:
        pytest.skip('Needs supports_open_accept_channel_types')

    local_funding_privkey = '20'

    local_keyset = KeySet(revocation_base_secret='21',
                          payment_base_secret='22',
                          htlc_base_secret='24',
                          delayed_payment_base_secret='23',
                          shachain_seed='00' * 32)

    test = [
        Block(blockheight=102, txs=[tx_spendable]),
        Connect(connprivkey='02'),
        ExpectMsg('init'),
        TryAll(
            # BOLT-a12da24dd0102c170365124782b46d9710950ac1 #9:
            # | 20/21 | `option_anchor_outputs`          | Anchor outputs
            Msg('init', globalfeatures='', features=bitfield(13, 21)),
            # BOLT #9:
            # | 12/13 | `option_static_remotekey`        | Static key for remote output
            Msg('init', globalfeatures='', features=bitfield(13)),
            # And not.
            Msg('init', globalfeatures='', features='')),
        Msg(
            'open_channel',
            chain_hash=regtest_hash,
            temporary_channel_id='00' * 32,
            funding_satoshis=funding_amount_for_utxo(0),
            push_msat=0,
            dust_limit_satoshis=546,
            max_htlc_value_in_flight_msat=4294967295,
            channel_reserve_satoshis=9998,
            htlc_minimum_msat=0,
            feerate_per_kw=253,
            # We use 5, because c-lightning runner uses 6, so this is different.
            to_self_delay=5,
            max_accepted_htlcs=483,
            funding_pubkey=pubkey_of(local_funding_privkey),
            revocation_basepoint=local_keyset.revocation_basepoint(),
            payment_basepoint=local_keyset.payment_basepoint(),
            delayed_payment_basepoint=local_keyset.delayed_payment_basepoint(),
            htlc_basepoint=local_keyset.htlc_basepoint(),
            first_per_commitment_point=local_keyset.per_commit_point(0),
            channel_flags=1,
            tlvs='{channel_types={types=[{features=' + bitfield(1) + '}]}}'),

        # BOLT #2
        # The receiving node MUST fail the channel if:
        #   - It supports `channel_types` and none of the `channel_types`
        #     are suitable.
        ExpectError()
    ]

    runner.run(test)
Example #2
0
def test_query_channel_range(runner: Runner) -> None:
    if runner.has_option('option_gossip_queries') is None:
        unittest.SkipTest('Needs option_gossip_queries')

    funding1, funding1_tx = Funding.from_utxo(*utxo(0),
                                              local_node_privkey='02',
                                              local_funding_privkey='10',
                                              remote_node_privkey='03',
                                              remote_funding_privkey='20')

    funding2, funding2_tx = Funding.from_utxo(*utxo(1),
                                              local_node_privkey='04',
                                              local_funding_privkey='30',
                                              remote_node_privkey='05',
                                              remote_funding_privkey='40')

    timestamp_103x1x0_LOCAL = int(time.time())
    timestamp_109x1x0_LOCAL = timestamp_103x1x0_LOCAL - 1
    timestamp_109x1x0_REMOTE = timestamp_109x1x0_LOCAL - 1

    ts_103x1x0 = encode_timestamps(
        *funding1.node_id_sort(timestamp_103x1x0_LOCAL, 0))
    ts_109x1x0 = encode_timestamps(*funding2.node_id_sort(
        timestamp_109x1x0_LOCAL, timestamp_109x1x0_REMOTE))

    update_103x1x0_LOCAL = funding1.channel_update(
        side=Side.local,
        short_channel_id='103x1x0',
        disable=False,
        cltv_expiry_delta=144,
        htlc_minimum_msat=0,
        fee_base_msat=1000,
        fee_proportional_millionths=10,
        timestamp=timestamp_103x1x0_LOCAL,
        htlc_maximum_msat=None)
    update_109x1x0_LOCAL = funding2.channel_update(
        side=Side.local,
        short_channel_id='109x1x0',
        disable=False,
        cltv_expiry_delta=144,
        htlc_minimum_msat=0,
        fee_base_msat=1000,
        fee_proportional_millionths=10,
        timestamp=timestamp_109x1x0_LOCAL,
        htlc_maximum_msat=None)
    update_109x1x0_REMOTE = funding2.channel_update(
        side=Side.remote,
        short_channel_id='109x1x0',
        disable=False,
        cltv_expiry_delta=144,
        htlc_minimum_msat=0,
        fee_base_msat=1000,
        fee_proportional_millionths=10,
        timestamp=timestamp_109x1x0_REMOTE,
        htlc_maximum_msat=None)

    csums_103x1x0 = update_checksums(
        *funding1.node_id_sort(update_103x1x0_LOCAL, None))
    csums_109x1x0 = update_checksums(
        *funding2.node_id_sort(update_109x1x0_LOCAL, update_109x1x0_REMOTE))

    test = [
        Block(blockheight=102, txs=[tx_spendable]),
        # Channel 103x1x0 (between 002 and 003)
        Block(blockheight=103, number=6, txs=[funding1_tx]),
        # Channel 109x1x0 (between 004 and 005)
        Block(blockheight=109, number=6, txs=[funding2_tx]),
        Connect(connprivkey='03'),
        ExpectMsg('init'),
        Msg('init', globalfeatures='', features=''),
        RawMsg(funding1.channel_announcement('103x1x0', '')),
        RawMsg(update_103x1x0_LOCAL),
        RawMsg(funding2.channel_announcement('109x1x0', '')),
        RawMsg(update_109x1x0_LOCAL),
        RawMsg(update_109x1x0_REMOTE),

        # c-lightning gets a race condition if we dont wait for
        # these updates to be added to the gossip store
        # FIXME: convert to explicit signal
        Wait(0.7),

        # New peer connects, with gossip_query option.
        Connect(connprivkey='05'),
        ExpectMsg('init'),
        # BOLT #9:
        # | 6/7   | `gossip_queries`                 | More sophisticated gossip control
        Msg('init', globalfeatures='', features=bitfield(7)),
        TryAll(
            # No queries?  Must not get anything.
            [
                MustNotMsg('channel_announcement'),
                MustNotMsg('channel_update'),
                MustNotMsg('node_announcement')
            ],

            # This should elicit an empty response
            [
                Msg('query_channel_range',
                    chain_hash=regtest_hash,
                    first_blocknum=0,
                    number_of_blocks=103),
                ExpectMsg('reply_channel_range',
                          chain_hash=regtest_hash,
                          first_blocknum=0,
                          number_of_blocks=103),
                CheckEq(decode_scids, '')
            ],

            # This should get the first one, not the second.
            [
                Msg('query_channel_range',
                    chain_hash=regtest_hash,
                    first_blocknum=103,
                    number_of_blocks=1),
                ExpectMsg('reply_channel_range',
                          chain_hash=regtest_hash,
                          first_blocknum=103,
                          number_of_blocks=1),
                CheckEq(decode_scids, '103x1x0')
            ],

            # This should get the second one, not the first.
            [
                Msg('query_channel_range',
                    chain_hash=regtest_hash,
                    first_blocknum=109,
                    number_of_blocks=4294967295),
                OneOf(
                    ExpectMsg('reply_channel_range',
                              chain_hash=regtest_hash,
                              first_blocknum=109,
                              number_of_blocks=4294967186),
                    # Could truncate number_of_blocks.
                    ExpectMsg('reply_channel_range',
                              chain_hash=regtest_hash,
                              first_blocknum=109,
                              number_of_blocks=1)),
                CheckEq(decode_scids, '109x1x0')
            ],

            # This should get both.
            [
                Msg('query_channel_range',
                    chain_hash=regtest_hash,
                    first_blocknum=103,
                    number_of_blocks=7),
                ExpectMsg('reply_channel_range',
                          chain_hash=regtest_hash,
                          first_blocknum=103,
                          number_of_blocks=7),
                CheckEq(decode_scids, '103x1x0,109x1x0')
            ],

            # This should get appended timestamp fields with option_gossip_queries_ex
            Sequence(enable=runner.has_option('option_gossip_queries_ex')
                     is not None,
                     events=[
                         Msg('query_channel_range',
                             chain_hash=regtest_hash,
                             first_blocknum=103,
                             number_of_blocks=7,
                             tlvs='{query_option={query_option_flags=1}}'),
                         ExpectMsg('reply_channel_range',
                                   chain_hash=regtest_hash,
                                   first_blocknum=103,
                                   number_of_blocks=7),
                         CheckEq(decode_timestamps, ts_103x1x0 + ts_109x1x0),
                         CheckEq(decode_scids, '103x1x0,109x1x0')
                     ]),

            # This should get appended checksum fields with option_gossip_queries_ex
            Sequence(enable=runner.has_option('option_gossip_queries_ex')
                     is not None,
                     events=[
                         Msg('query_channel_range',
                             chain_hash=regtest_hash,
                             first_blocknum=103,
                             number_of_blocks=7,
                             tlvs='{query_option={query_option_flags=2}}'),
                         ExpectMsg('reply_channel_range',
                                   chain_hash=regtest_hash,
                                   first_blocknum=103,
                                   number_of_blocks=7,
                                   tlvs='{checksums_tlv={checksums=[' +
                                   csums_103x1x0 + ',' + csums_109x1x0 +
                                   ']}}'),
                         CheckEq(decode_scids, '103x1x0,109x1x0')
                     ]),

            # This should append timestamps and checksums with option_gossip_queries_ex
            Sequence(enable=runner.has_option('option_gossip_queries_ex')
                     is not None,
                     events=[
                         Msg('query_channel_range',
                             chain_hash=regtest_hash,
                             first_blocknum=103,
                             number_of_blocks=7,
                             tlvs='{query_option={query_option_flags=3}}'),
                         ExpectMsg('reply_channel_range',
                                   chain_hash=regtest_hash,
                                   first_blocknum=103,
                                   number_of_blocks=7,
                                   tlvs='{checksums_tlv={checksums=[' +
                                   csums_103x1x0 + ',' + csums_109x1x0 +
                                   ']}}'),
                         CheckEq(decode_timestamps, ts_103x1x0 + ts_109x1x0),
                         CheckEq(decode_scids, '103x1x0,109x1x0')
                     ]))
    ]

    runner.run(test)
Example #3
0
def test_init(runner: Runner, namespaceoverride: Any) -> None:
    # We override default namespace since we only need BOLT1
    namespaceoverride(pyln.spec.bolt1.namespace)
    test = [
        Connect(connprivkey='03'),
        ExpectMsg('init'),
        Msg('init', globalfeatures='', features=''),

        # optionally disconnect that first one
        TryAll([], Disconnect()),
        Connect(connprivkey='02'),
        TryAll(
            # Even if we don't send anything, it should send init.
            [ExpectMsg('init')],

            # Minimal possible init message.
            # BOLT #1:
            # The sending node:
            #  - MUST send `init` as the first Lightning message for any connection.
            [ExpectMsg('init'),
             Msg('init', globalfeatures='', features='')],

            # BOLT #1:
            # The sending node:...
            #  - SHOULD NOT set features greater than 13 in `globalfeatures`.
            [
                ExpectMsg('init', if_match=no_gf13),
                # BOLT #1:
                # The receiving node:...
                #  - upon receiving unknown _odd_ feature bits that are non-zero:
                #    - MUST ignore the bit.

                # init msg with unknown odd global bit (99): no error
                Msg('init', globalfeatures=bitfield(99), features='')
            ],

            # Sanity check that bits 34 and 35 are not used!
            [
                ExpectMsg('init',
                          if_match=functools.partial(no_feature, [34, 35])),
                # BOLT #1:
                # The receiving node:...
                #  - upon receiving unknown _odd_ feature bits that are non-zero:
                #    - MUST ignore the bit.

                # init msg with unknown odd local bit (99): no error
                Msg('init', globalfeatures='', features=bitfield(99))
            ],

            # BOLT #1:
            # The receiving node: ...
            #  - upon receiving unknown _even_ feature bits that are non-zero:
            #    - MUST fail the connection.
            [
                ExpectMsg('init'),
                Msg('init', globalfeatures='', features=bitfield(34)),
                ExpectError()
            ],

            # init msg with unknown even global bit (34): you will error
            [
                ExpectMsg('init'),
                Msg('init', globalfeatures=bitfield(34), features=''),
                ExpectError()
            ],

            # If you don't support `option_data_loss_protect`, you will be ok if
            # we ask for it.
            Sequence([
                ExpectMsg('init',
                          if_match=functools.partial(no_feature, [0, 1])),
                Msg('init', globalfeatures='', features=bitfield(1))
            ],
                     enable=not runner.has_option('option_data_loss_protect')),

            # If you don't support `option_data_loss_protect`, you will error if
            # we require it.
            Sequence([
                ExpectMsg('init',
                          if_match=functools.partial(no_feature, [0, 1])),
                Msg('init', globalfeatures='', features=bitfield(0)),
                ExpectError()
            ],
                     enable=not runner.has_option('option_data_loss_protect')),

            # If you support `option_data_loss_protect`, you will advertize it odd.
            Sequence(
                [
                    ExpectMsg('init',
                              if_match=functools.partial(has_feature, [1]))
                ],
                enable=(
                    runner.has_option('option_data_loss_protect') == 'odd')),

            # If you require `option_data_loss_protect`, you will advertize it even.
            Sequence(
                [
                    ExpectMsg('init',
                              if_match=functools.partial(has_feature, [0]))
                ],
                enable=(
                    runner.has_option('option_data_loss_protect') == 'even')),

            # If you don't support `option_anchor_outputs`, you will be ok if
            # we ask for it.
            Sequence([
                ExpectMsg('init',
                          if_match=functools.partial(no_feature, [20, 21])),
                Msg('init', globalfeatures='', features=bitfield(21))
            ],
                     enable=not runner.has_option('option_anchor_outputs')),

            # If you don't support `option_anchor_outputs`, you will error if
            # we require it.
            Sequence([
                ExpectMsg('init',
                          if_match=functools.partial(no_feature, [20, 21])),
                Msg('init', globalfeatures='', features=bitfield(20)),
                ExpectError()
            ],
                     enable=not runner.has_option('option_anchor_outputs')),

            # If you support `option_anchor_outputs`, you will advertize it odd.
            Sequence(
                [
                    ExpectMsg('init',
                              if_match=functools.partial(has_feature, [21]))
                ],
                enable=(runner.has_option('option_anchor_outputs') == 'odd')),

            # If you require `option_anchor_outputs`, you will advertize it even.
            Sequence(
                [
                    ExpectMsg('init',
                              if_match=functools.partial(has_feature, [20]))
                ],
                enable=(runner.has_option('option_anchor_outputs') == 'even')),

            # BOLT-a12da24dd0102c170365124782b46d9710950ac1 #9:
            # | Bits  | Name                    | ... | Dependencies
            # ...
            # | 12/13 | `option_static_remotekey` |
            # ...
            # | 20/21 | `option_anchor_outputs` | ... | `option_static_remotekey` |

            # If you support `option_anchor_outputs`, you will
            # advertize option_static_remotekey.
            Sequence([
                ExpectMsg('init',
                          if_match=functools.partial(has_one_feature,
                                                     [12, 13]))
            ],
                     enable=(runner.has_option('option_anchor_outputs')
                             is not None)),

            # You should always handle us echoing your own features back!
            [
                ExpectMsg('init'),
                Msg('init', globalfeatures=rcvd(), features=rcvd())
            ],
        )
    ]

    runner.run(test)
Example #4
0
def test_gossip_timestamp_filter(runner: Runner) -> None:
    if runner.has_option('option_gossip_queries') is None:
        unittest.SkipTest('Needs option_gossip_queries')

    funding1, funding1_tx = Funding.from_utxo(*utxo(0),
                                              local_node_privkey='02',
                                              local_funding_privkey='10',
                                              remote_node_privkey='03',
                                              remote_funding_privkey='20')

    funding2, funding2_tx = Funding.from_utxo(*utxo(1),
                                              local_node_privkey='04',
                                              local_funding_privkey='30',
                                              remote_node_privkey='05',
                                              remote_funding_privkey='40')

    timestamp1 = int(time.time())
    timestamp2 = timestamp1 + 1

    test = [Block(blockheight=102, txs=[tx_spendable]),

            Connect(connprivkey='03'),
            ExpectMsg('init'),
            Msg('init', globalfeatures='', features=''),

            # txid 189c40b0728f382fe91c87270926584e48e0af3a6789f37454afee6c7560311d
            Block(blockheight=103, number=6, txs=[funding1_tx]),

            RawMsg(funding1.channel_announcement('103x1x0', '')),
            RawMsg(funding1.node_announcement(Side.local, '', (1, 2, 3), 'foobar', b'', timestamp1)),

            # New peer connects, asks for gossip_timestamp_filter=all.  We *won't* relay channel_announcement, as there is no channel_update.
            Connect(connprivkey='05'),
            ExpectMsg('init'),
            # BOLT #9:
            # | 6/7   | `gossip_queries`                 | More sophisticated gossip control
            Msg('init', globalfeatures='', features=bitfield(6)),
            Msg('gossip_timestamp_filter', chain_hash='06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f',
                first_timestamp=0, timestamp_range=4294967295),
            MustNotMsg('channel_announcement'),
            MustNotMsg('channel_update'),
            MustNotMsg('node_announcement'),
            Disconnect(),

            # Now, with channel update
            RawMsg(funding1.channel_update(side=Side.local,
                                           short_channel_id='103x1x0',
                                           disable=False,
                                           cltv_expiry_delta=144,
                                           htlc_minimum_msat=0,
                                           fee_base_msat=1000,
                                           fee_proportional_millionths=10,
                                           timestamp=timestamp1,
                                           htlc_maximum_msat=None),
                   connprivkey='03'),

            # New peer connects, asks for gossip_timestamp_filter=all.  update and node announcement will be relayed.
            Connect(connprivkey='05'),
            ExpectMsg('init'),
            Msg('init', globalfeatures='', features=bitfield(6)),
            Msg('gossip_timestamp_filter', chain_hash='06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f',
                first_timestamp=0, timestamp_range=4294967295),

            ExpectMsg('channel_announcement', short_channel_id='103x1x0'),
            AnyOrder(ExpectMsg('channel_update', short_channel_id='103x1x0'),
                     ExpectMsg('node_announcement')),
            Disconnect(),

            # BOLT #7:
            # The receiver:
            #  - SHOULD send all gossip messages whose `timestamp` is greater or
            #    equal to `first_timestamp`, and less than `first_timestamp` plus
            #    `timestamp_range`.
            Connect(connprivkey='05'),
            ExpectMsg('init'),
            Msg('init', globalfeatures='', features=bitfield(6)),
            Msg('gossip_timestamp_filter', chain_hash='06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f',
                first_timestamp=1000, timestamp_range=timestamp1 - 1000),

            MustNotMsg('channel_announcement'),
            MustNotMsg('channel_update'),
            MustNotMsg('node_announcement'),
            Disconnect(),

            Connect(connprivkey='05'),
            ExpectMsg('init'),
            Msg('init', globalfeatures='', features=bitfield(6)),
            Msg('gossip_timestamp_filter', chain_hash='06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f',
                first_timestamp=timestamp1 + 1, timestamp_range=4294967295),

            MustNotMsg('channel_announcement'),
            MustNotMsg('channel_update'),
            MustNotMsg('node_announcement'),
            Disconnect(),

            # These two succeed in getting the gossip, then stay connected for next test.
            Connect(connprivkey='05'),
            ExpectMsg('init'),
            Msg('init', globalfeatures='', features=bitfield(6)),
            Msg('gossip_timestamp_filter', chain_hash='06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f',
                first_timestamp=timestamp1, timestamp_range=4294967295),

            ExpectMsg('channel_announcement', short_channel_id='103x1x0'),
            AnyOrder(ExpectMsg('channel_update', short_channel_id='103x1x0'),
                     ExpectMsg('node_announcement')),

            Connect(connprivkey='06'),
            ExpectMsg('init'),
            Msg('init', globalfeatures='', features=bitfield(6)),
            Msg('gossip_timestamp_filter', chain_hash='06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f',
                first_timestamp=1000, timestamp_range=timestamp1 - 1000 + 1),

            ExpectMsg('channel_announcement', short_channel_id='103x1x0'),
            AnyOrder(ExpectMsg('channel_update', short_channel_id='103x1x0'),
                     ExpectMsg('node_announcement')),


            # BOLT #7:
            #  - SHOULD restrict future gossip messages to those whose `timestamp`
            #    is greater or equal to `first_timestamp`, and less than
            #    `first_timestamp` plus `timestamp_range`.
            Block(blockheight=109, number=6, txs=[funding2_tx]),

            RawMsg(funding2.channel_announcement('109x1x0', ''),
                   connprivkey='03'),
            RawMsg(funding2.channel_update(side=Side.local,
                                           short_channel_id='109x1x0',
                                           disable=False,
                                           cltv_expiry_delta=144,
                                           htlc_minimum_msat=0,
                                           fee_base_msat=1000,
                                           fee_proportional_millionths=10,
                                           timestamp=timestamp2,
                                           htlc_maximum_msat=None)),
            RawMsg(funding2.channel_update(side=Side.remote,
                                           short_channel_id='109x1x0',
                                           disable=False,
                                           cltv_expiry_delta=144,
                                           htlc_minimum_msat=0,
                                           fee_base_msat=1000,
                                           fee_proportional_millionths=10,
                                           timestamp=timestamp2,
                                           htlc_maximum_msat=None)),
            RawMsg(funding2.node_announcement(Side.local, '', (1, 2, 3), 'foobar2', b'', timestamp2)),

            # 005's filter covers this, 006's doesn't.
            ExpectMsg('channel_announcement', short_channel_id='109x1x0', connprivkey='05'),
            AnyOrder(ExpectMsg('channel_update', short_channel_id='109x1x0', channel_flags=0),
                     ExpectMsg('channel_update', short_channel_id='109x1x0', channel_flags=1),
                     ExpectMsg('node_announcement')),

            MustNotMsg('channel_announcement', connprivkey='06'),
            MustNotMsg('channel_update'),
            MustNotMsg('node_announcement')]

    runner.run(test)