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)
def test_unknowns(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=''), TryAll( [], # BOLT #1: # A receiving node: # - upon receiving a message of _odd_, unknown type: # - MUST ignore the received message. [RawMsg(bytes.fromhex('270F'))], # BOLT #1: # A receiving node:... # - upon receiving a message of _even_, unknown type: # - MUST close the connection. # - MAY fail the channels. [RawMsg(bytes.fromhex('2710')), ExpectError()]) ] runner.run(test)
def test_namespace_override(runner: Runner, namespaceoverride: Any) -> None: # Truncate the namespace to just BOLT1 namespaceoverride(pyln.spec.bolt1.namespace) # Try to send a message that's not in BOLT1 with pytest.raises(SpecFileError, match=r'Unknown msgtype open_channel'): Msg('open_channel')
def test_open_channel(runner: Runner) -> None: 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 #9: # | 12/13 | `option_static_remotekey` | Static key for remote output Msg('init', globalfeatures='', features=bitfield(13)), # And not. Msg('init', globalfeatures='', features='')), TryAll( # Accepter side: we initiate a new channel. [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), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('accept_channel', temporary_channel_id=sent(), funding_pubkey=remote_funding_pubkey(), revocation_basepoint=remote_revocation_basepoint(), payment_basepoint=remote_payment_basepoint(), delayed_payment_basepoint=remote_delayed_payment_basepoint(), htlc_basepoint=remote_htlc_basepoint(), first_per_commitment_point=remote_per_commitment_point(0), minimum_depth=3, channel_reserve_satoshis=9998), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), # Create and stash Funding object and FundingTx CreateFunding(*utxo(0), local_node_privkey='02', local_funding_privkey=local_funding_privkey, remote_node_privkey=runner.get_node_privkey(), remote_funding_privkey=remote_funding_privkey()), Commit(funding=funding(), opener=Side.local, local_keyset=local_keyset, local_to_self_delay=rcvd('to_self_delay', int), remote_to_self_delay=sent('to_self_delay', int), local_amount=msat(sent('funding_satoshis', int)), remote_amount=0, local_dust_limit=546, remote_dust_limit=546, feerate=253, local_features=sent('init.features'), remote_features=rcvd('init.features')), Msg('funding_created', temporary_channel_id=rcvd(), funding_txid=funding_txid(), funding_output_index=0, signature=commitsig_to_send()), ExpectMsg('funding_signed', channel_id=channel_id(), signature=commitsig_to_recv()), # Mine it and get it deep enough to confirm channel. Block(blockheight=103, number=3, txs=[funding_tx()]), ExpectMsg('funding_locked', channel_id=channel_id(), next_per_commitment_point='032405cbd0f41225d5f203fe4adac8401321a9e05767c5f8af97d51d2e81fbb206'), Msg('funding_locked', channel_id=channel_id(), next_per_commitment_point='027eed8389cf8eb715d73111b73d94d2c2d04bf96dc43dfd5b0970d80b3617009d'), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F')))], # Now we test the 'opener' side of an open_channel (node initiates) [FundChannel(amount=999877), # This gives a channel of 999877sat ExpectMsg('open_channel', chain_hash=regtest_hash, funding_satoshis=999877, push_msat=0, dust_limit_satoshis=546, htlc_minimum_msat=0, channel_reserve_satoshis=9998, to_self_delay=6, funding_pubkey=remote_funding_pubkey(), revocation_basepoint=remote_revocation_basepoint(), payment_basepoint=remote_payment_basepoint(), delayed_payment_basepoint=remote_delayed_payment_basepoint(), htlc_basepoint=remote_htlc_basepoint(), first_per_commitment_point=remote_per_commitment_point(0), # FIXME: Check more fields! channel_flags='01'), Msg('accept_channel', temporary_channel_id=rcvd(), dust_limit_satoshis=546, max_htlc_value_in_flight_msat=4294967295, channel_reserve_satoshis=9998, htlc_minimum_msat=0, minimum_depth=3, max_accepted_htlcs=483, # We use 5, because c-lightning runner uses 6, so this is different. to_self_delay=5, 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)), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('funding_created', temporary_channel_id=rcvd('temporary_channel_id')), # Now we can finally stash the funding information. AcceptFunding(rcvd('funding_created.funding_txid'), funding_output_index=rcvd('funding_created.funding_output_index', int), funding_amount=rcvd('open_channel.funding_satoshis', int), local_node_privkey='02', local_funding_privkey=local_funding_privkey, remote_node_privkey=runner.get_node_privkey(), remote_funding_privkey=remote_funding_privkey()), Commit(funding=funding(), opener=Side.remote, local_keyset=local_keyset, local_to_self_delay=rcvd('open_channel.to_self_delay', int), remote_to_self_delay=sent('accept_channel.to_self_delay', int), local_amount=0, remote_amount=msat(rcvd('open_channel.funding_satoshis', int)), local_dust_limit=sent('accept_channel.dust_limit_satoshis', int), remote_dust_limit=rcvd('open_channel.dust_limit_satoshis', int), feerate=rcvd('open_channel.feerate_per_kw', int), local_features=sent('init.features'), remote_features=rcvd('init.features')), # Now we've created commit, we can check sig is valid! CheckEq(rcvd('funding_created.signature'), commitsig_to_recv()), Msg('funding_signed', channel_id=channel_id(), signature=commitsig_to_send()), # It will broadcast tx ExpectTx(rcvd('funding_created.funding_txid')), # Mine three blocks to confirm channel. Block(blockheight=103, number=3), Msg('funding_locked', channel_id=sent(), next_per_commitment_point=local_keyset.per_commit_point(1)), ExpectMsg('funding_locked', channel_id=sent(), next_per_commitment_point=remote_per_commitment_point(1)), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ])] runner.run(test)
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)
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)
def test_reestablish(runner: Runner) -> None: 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( Msg('init', globalfeatures='', features=bitfield(data_loss_protect)), Msg('init', globalfeatures='', features=bitfield(static_remotekey)), # And nothing. 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, # clightning uses to_self_delay=6; we use 5 to test differentiation 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), ExpectMsg('accept_channel', funding_pubkey=remote_funding_pubkey(), revocation_basepoint=remote_revocation_basepoint(), payment_basepoint=remote_payment_basepoint(), delayed_payment_basepoint=remote_delayed_payment_basepoint(), htlc_basepoint=remote_htlc_basepoint(), first_per_commitment_point=remote_per_commitment_point(0), minimum_depth=3, channel_reserve_satoshis=9998), # Create and stash Funding object and FundingTx CreateFunding(*utxo(0), local_node_privkey='02', local_funding_privkey=local_funding_privkey, remote_node_privkey=runner.get_node_privkey(), remote_funding_privkey=remote_funding_privkey()), Commit(funding=funding(), opener=Side.local, local_keyset=local_keyset, local_to_self_delay=rcvd('to_self_delay', int), remote_to_self_delay=sent('to_self_delay', int), local_amount=msat(sent('funding_satoshis', int)), remote_amount=0, local_dust_limit=546, remote_dust_limit=546, feerate=253, local_features=sent('init.features'), remote_features=rcvd('init.features')), Msg('funding_created', temporary_channel_id=rcvd(), funding_txid=funding_txid(), funding_output_index=0, signature=commitsig_to_send()), ExpectMsg('funding_signed', channel_id=channel_id(), signature=commitsig_to_recv()), # Mine it and get it deep enough to confirm channel. Block(blockheight=103, number=3, txs=[funding_tx()]), ExpectMsg('funding_locked', channel_id=channel_id(), next_per_commitment_point=remote_per_commitment_point(1)), Msg('funding_locked', channel_id=channel_id(), next_per_commitment_point=local_keyset.per_commit_point(1)), Disconnect(), Connect(connprivkey='02'), ExpectMsg('init'), # Reconnect with same features. Msg('init', globalfeatures='', features=sent('init.features')), # BOLT #2: # - if `next_revocation_number` equals 0: # - MUST set `your_last_per_commitment_secret` to all zeroes # - otherwise: # - MUST set `your_last_per_commitment_secret` to the last # `per_commitment_secret` it received ExpectMsg('channel_reestablish', channel_id=channel_id(), next_commitment_number=1, next_revocation_number=0, your_last_per_commitment_secret='00' * 32), # BOLT #2: # The sending node:... # - if `option_static_remotekey` applies to the commitment # transaction: # - MUST set `my_current_per_commitment_point` to a valid point. # - otherwise: # - MUST set `my_current_per_commitment_point` to its commitment # point for the last signed commitment it received from its # channel peer (i.e. the commitment_point corresponding to the # commitment transaction the sender would use to unilaterally # close). Sequence(CheckEq(rcvd('my_current_per_commitment_point'), remote_per_commitment_point(0)), enable=negotiated(sent('init.features'), rcvd('init.features'), excluded=[static_remotekey])), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), Msg('channel_reestablish', channel_id=channel_id(), next_commitment_number=1, next_revocation_number=0, your_last_per_commitment_secret='00' * 32, my_current_per_commitment_point=local_keyset.per_commit_point(0)), # FIXME: Check that they error and unilateral close if we give # the wrong info! ] runner.run(test)
def test_htlc_add(runner: Runner) -> None: 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) # FIXME: Generate onion routing packet! dust_htlc = HTLC( owner=Side.local, amount_msat=1000, payment_secret='00' * 32, cltv_expiry=200, # hop_data[0] = 00000000000000000000000000000003E8000000C8000000000000000000000000 onion_routing_packet= '0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619b1153ae94698ea83a3298ab3c0ddd9f755853e4e5fbc5d4f3cb457bbb74a9b81d3b5bc9cf42d8617d1fe6966ffb66b8ec0eaa1188865957e26df123d11705395d339472bcc4920e428492f7822424eef8e6d903a768ec01959f3a1f2c1cd8725ba13329df3a932f641dee600dbb1a9f3bbe93a167410961f1777a7b48679d8a3041d57c0b8e795ed4884fbb33a2564d4cdafb528c7b63fc31cd2739e71d1d3b56f35ba7976a373b883eed8f1f263aedd540cce9b548e53e58c32ab604195f6004d8d92fe0a9a454229b9bc0795f3e4ccd54089075483afaa0ef3b32ee12cf321052f7b9e5ac1c28169e57d5628c3aee5c775d5fb33ba835fda195981b1e3a06792bdd0ecf85f8f6107fd830ca932e92c6713ea6d4d5129395f54aeabb54debccca130ad019a1f53a20c0c46dd8625ada068e2a13ea5373b60ecdf412728cc78192ae1a56bae26dfb450d2f6b4905e6bd9843fda7df63eb11fb77ce995b25d3076210eca527bb556b4ddc564fa4c6ccb43f1149163a4959ffe4178d653d35bdc052e4a46dd58b8f95fde83d114c4e35fd02e94a0dd2a9ae21594184808074a57d9de30c5105b53efe03aca192f8c518bc2b9e13211a9761c1948b31aa97f99da449968380005f96ff49a6e5fe833220a82f358eb94197584b2dfa5a1efee8918b5020f028748e5897bb694979f580ff58b8b1d865783340eaff2d1ce738409ec1c62c1bd7f632cf0730a5634a1a2d91244b865302339c1861655e11b264aeaf2feefbf2d1222bb13c6bd6b2d2379d9a548f93de4d2a044928458eafa745021e0a69796bb40f17c1ca53b895c76b53924faa886a4a19f07b50eda5f316e5f3b5422e984c59928144c275d4ae5e78634e16c6dafcfc92bb302c7d5eef1456250b0b8a41f0cabb55dd114d6b0bcaf53ef1ee2185d2383df57a0f1bc21d31f5d3ae395bab6e77370ee83ffe8995e9bfbe2f90b3ff0578720e0584e969479d40327415835579d7b8885037c02a611292c6bbffde25e86c184cc7c7481e8856ce6a3cf7109a6c001e51a2289c5ee3633936578d4dc3de82c18ebb787bf2c475e8fa0393727cbdbcd36849ee0b7411fba6fd5cb8459e63aaf3fba7a4cd4a04b266d8f416f0586e2093ea9c210140a6e6cb72759ae1dee7c24497f68389fb8d154f927cc4ab59b9137652eaf9c7cb56f0cce6c58616646c6fee836b07ce738a965b1ea725d9960c47e61086be053f3e9c48c08ce945404b060d9e699ad962c910208dda42d665f8eacf9865a64d2612ea62e0e2c0a4c731b35ae87b04e45739c34f4c972ce433a2094b10a9601e6711b95a6a226a85f4e4ed0e0417dbc9d737cd7d3513a82943de94ff8e4c9e91838506283f4878e3f41488fec47198b4a262b55d3691d275c6154d2a2ce9ee6ab97087e0f33654b01450869797c993dfca76cd732677bf1856f43d040d68022055987588f64af357bea80491b4bc42341dd6f81631d30fc28e8c5d7e3312655b30d277f10ce76c2525279ad53157b1c2c78b412107fc5f974ac7946bdc33ee54d71f3fc261530d50f20813e4e6aadf39e67573d5dc93a45023edf297b56def6b14ec5e19ca10fbfd1b807f17fa983bec363cf495c708a581db1bba1a23730ce22d0f925d764b04be014d662c3a36ac58b015317c9cf5ca6464f2ecef15e1769f2c91922968532bda66e9aaa2a7f120a9301f563fd33db8e90c940984b0a297e0c595544b7f687476325a07dbaba255c8461e98f069eea2246cfa50f1c2ef8d4c54f5fd509a9cc839548d7c252e60bb9c165d05f30bd525f6b53a4c8afc8fc31026686bcd5a48172593941b3113cbed88e6cfb566f7a693bb63c9a89925c1f5df0a115b4893128866a81c1b' ) non_dust_htlc = HTLC( owner=Side.local, amount_msat=1000000, payment_secret='00' * 32, cltv_expiry=200, onion_routing_packet= '0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619b1153ae94698ea83a3298ab3c0ddd9f755853e4e5fbc5d4f3cb457bbb74a9b81d3b5bc9cf42d8617d1fe6966ffb66b8ec0eaa1188865957e26df123d11705395d339472bcc4920e428492f7822424eef8e6d903a768ec01959f3a1f2c1cd8725ba13329df3a932f641dee600dbb1a9f3bbe93a167410961f1777a7b48679d8a3041d57c0b8e795ed4884fbb33a2564d4cdafb528c7b63fc31cd2739e71d1d3b56f35ba7976a373b883eed8f1f263aedd540cce9b548e53e58c32ab604195f6004d8d92fe0a9a454229b9bc0795f3e4ccd54089075483afaa0ef3b32ee12cf321052f7b9e5ac1c28169e57d5628c3aee5c775d5fb33ba835fda195981b1e3a06792bdd0ecf85f8f6107fd830ca932e92c6713ea6d4d5129395f54aeabb54debccca130ad019a1f53a20c0c46dd8625ada068e2a13ea5373b60ecdf412728cc78192ae1a56bae26dfb450d2f6b4905e6bd9843fda7df63eb11fb77ce995b25d3076210eca527bb556b4ddc564fa4c6ccb43f1149163a4959ffe4178d653d35bdc052e4a46dd58b8f95fde83d114c4e35fd02e94a0dd2a9ae21594184808074a57d9de30c5105b53efe03aca192f8c518bc2b9e13211a9761c1948b31aa97f99da449968380005f96ff49a6e5fe833220a82f358eb94197584b2dfa5a1efee8918b5020f028748e5897bb694979f580ff58b8b1d865783340eaff2d1ce738409ec1c62c1bd7f632cf0730a5634a1a2d91244b865302339c1861655e11b264aeaf2feefbf2d1222bb13c6bd6b2d2379d9a548f93de4d2a044928458eafa745021e0a69796bb40f17c1ca53b895c76b53924faa886a4a19f07b50eda5f316e5f3b5422e984c59928144c275d4ae5e78634e16c6dafcfc92bb302c7d5eef1456250b0b8a41f0cabb55dd114d6b0bcaf53ef1ee2185d2383df57a0f1bc21d31f5d3ae395bab6e77370ee83ffe8995e9bfbe2f90b3ff0578720e0584e969479d40327415835579d7b8885037c02a611292c6bbffde25e86c184cc7c7481e8856ce6a3cf7109a6c001e51a2289c5ee3633936578d4dc3de82c18ebb787bf2c475e8fa0393727cbdbcd36849ee0b7411fba6fd5cb8459e63aaf3fba7a4cd4a04b266d8f416f0586e2093ea9c210140a6e6cb72759ae1dee7c24497f68389fb8d154f927cc4ab59b9137652eaf9c7cb56f0cce6c58616646c6fee836b07ce738a965b1ea725d9960c47e61086be053f3e9c48c08ce945404b060d9e699ad962c910208dda42d665f8eacf9865a64d2612ea62e0e2c0a4c731b35ae87b04e45739c34f4c972ce433a2094b10a9601e6711b95a6a226a85f4e4ed0e0417dbc9d737cd7d3513a82943de94ff8e4c9e91838506283f4878e3f41488fec47198b4a262b55d3691d275c6154d2a2ce9ee6ab97087e0f33654b01450869797c993dfca76cd732677bf1856f43d040d68022055987588f64af357bea80491b4bc42341dd6f81631d30fc28e8c5d7e3312655b30d277f10ce76c2525279ad53157b1c2c78b412107fc5f974ac7946bdc33ee54d71f3fc261530d50f20813e4e6aadf39e67573d5dc93a45023edf297b56def6b14ec5e19ca10fbfd1b807f17fa983bec363cf495c708a581db1bba1a23730ce22d0f925d764b04be014d662c3a36ac58b015317c9cf5ca6464f2ecef15e1769f2c91922968532bda66e9aaa2a7f120a9301f563fd33db8e90c940984b0a297e0c595544b7f687476325a07dbaba255c8461e98f069eea2246cfa50f1c2ef8d4c54f5fd509a9cc839548d7c252e60bb9c165d05f30bd525f6b53a4c8afc8fc31026686bcd5a48172593941b3113cbed88e6cfb566f7a693bb63c9a89925c1f5df0a115b4893128866a81c1b' ) test = [ Block(blockheight=102, txs=[tx_spendable]), Connect(connprivkey='02'), ExpectMsg('init'), TryAll( Msg('init', globalfeatures='', features=bitfield(data_loss_protect)), Msg('init', globalfeatures='', features=bitfield(static_remotekey)), Msg('init', globalfeatures='', features=bitfield(static_remotekey, anchor_outputs)), # And nothing. 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, # clightning uses to_self_delay=6; we use 5 to test differentiation 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), ExpectMsg('accept_channel', funding_pubkey=remote_funding_pubkey(), revocation_basepoint=remote_revocation_basepoint(), payment_basepoint=remote_payment_basepoint(), delayed_payment_basepoint=remote_delayed_payment_basepoint(), htlc_basepoint=remote_htlc_basepoint(), first_per_commitment_point=remote_per_commitment_point(0), minimum_depth=3, channel_reserve_satoshis=9998), # Create and stash Funding object and FundingTx CreateFunding(*utxo(0), local_node_privkey='02', local_funding_privkey=local_funding_privkey, remote_node_privkey=runner.get_node_privkey(), remote_funding_privkey=remote_funding_privkey()), Commit(funding=funding(), opener=Side.local, local_keyset=local_keyset, local_to_self_delay=rcvd('to_self_delay', int), remote_to_self_delay=sent('to_self_delay', int), local_amount=msat(sent('funding_satoshis', int)), remote_amount=0, local_dust_limit=546, remote_dust_limit=546, feerate=253, local_features=sent('init.features'), remote_features=rcvd('init.features')), Msg('funding_created', temporary_channel_id=rcvd(), funding_txid=funding_txid(), funding_output_index=0, signature=commitsig_to_send()), ExpectMsg('funding_signed', channel_id=channel_id(), signature=commitsig_to_recv()), # Mine it and get it deep enough to confirm channel. Block(blockheight=103, number=3, txs=[funding_tx()]), ExpectMsg('funding_locked', channel_id=channel_id(), next_per_commitment_point=remote_per_commitment_point(1)), Msg('funding_locked', channel_id=channel_id(), next_per_commitment_point=local_keyset.per_commit_point(1)), # We try both a dust and a non-dust htlc. TryAll( Msg('update_add_htlc', channel_id=channel_id(), id=0, amount_msat=dust_htlc.amount_msat, payment_hash=dust_htlc.payment_hash(), cltv_expiry=dust_htlc.cltv_expiry, onion_routing_packet=dust_htlc.onion_routing_packet), Msg('update_add_htlc', channel_id=channel_id(), id=0, amount_msat=non_dust_htlc.amount_msat, payment_hash=non_dust_htlc.payment_hash(), cltv_expiry=non_dust_htlc.cltv_expiry, onion_routing_packet=non_dust_htlc.onion_routing_packet)), # Optional reconnect: TryAll( [], [ Disconnect(), Connect(connprivkey='02'), ExpectMsg('init'), # Reconnect with same features. Msg('init', globalfeatures='', features=sent('init.features')), ExpectMsg('channel_reestablish', channel_id=channel_id(), next_commitment_number=1, next_revocation_number=0, your_last_per_commitment_secret='00' * 32), Msg('channel_reestablish', channel_id=channel_id(), next_commitment_number=1, next_revocation_number=0, your_last_per_commitment_secret='00' * 32, my_current_per_commitment_point=local_keyset. per_commit_point(0)), # BOLT #2: # A node: # - if `next_commitment_number` is 1 in both the # `channel_reestablish` it sent and received: # - MUST retransmit `funding_locked`. # - otherwise: # - MUST NOT retransmit `funding_locked`. ExpectMsg( 'funding_locked', channel_id=channel_id(), next_per_commitment_point=remote_per_commitment_point(1), ignore=ExpectMsg.ignore_all_gossip), # BOLT #2: # A node: # ... # - upon disconnection: # - MUST reverse any uncommitted updates sent by the # other side (i.e. all messages beginning with `update_` # for which no `commitment_signed` has been received). # So this puts us back where we were. Msg('update_add_htlc', channel_id=channel_id(), id=0, amount_msat=dust_htlc.amount_msat, payment_hash=dust_htlc.payment_hash(), cltv_expiry=dust_htlc.cltv_expiry, onion_routing_packet=dust_htlc.onion_routing_packet) ]), UpdateCommit(new_htlcs=[(dust_htlc, 0)]), Msg('commitment_signed', channel_id=channel_id(), signature=commitsig_to_send(), htlc_signature=htlc_sigs_to_send()), ExpectMsg('revoke_and_ack', channel_id=channel_id(), per_commitment_secret=remote_per_commitment_secret(0), next_per_commitment_point=remote_per_commitment_point(2), ignore=ExpectMsg.ignore_all_gossip), ExpectMsg('commitment_signed', signature=commitsig_to_recv(), htlc_signature=htlc_sigs_to_recv(), ignore=ExpectMsg.ignore_all_gossip), # Now try optionally reconnecting. TryAll( [], # Ignore unknown. [RawMsg(bytes.fromhex('270F'))], [ Disconnect(), Connect(connprivkey='02'), ExpectMsg('init'), # Reconnect with same features. Msg('init', globalfeatures='', features=sent('init.features')), ExpectMsg('channel_reestablish', channel_id=channel_id(), next_commitment_number=2, next_revocation_number=0, your_last_per_commitment_secret='00' * 32, ignore=ExpectMsg.ignore_all_gossip), # Depends on what we tell them we already received: TryAll( # We didn't receive revoke_and_ack: [ Msg('channel_reestablish', channel_id=channel_id(), next_commitment_number=1, next_revocation_number=0, your_last_per_commitment_secret='00' * 32, my_current_per_commitment_point=local_keyset. per_commit_point(0)), ExpectMsg( 'revoke_and_ack', channel_id=channel_id(), per_commitment_secret=remote_per_commitment_secret( 0), next_per_commitment_point= remote_per_commitment_point(2), ignore=ExpectMsg.ignore_all_gossip), ExpectMsg('commitment_signed', signature=commitsig_to_recv(), htlc_signature=htlc_sigs_to_recv(), ignore=ExpectMsg.ignore_all_gossip) ], # We did receive revoke_and_ack, but not # commitment_signed [ Msg('channel_reestablish', channel_id=channel_id(), next_commitment_number=1, next_revocation_number=1, your_last_per_commitment_secret= remote_per_commitment_secret(0), my_current_per_commitment_point=local_keyset. per_commit_point(0)), ExpectMsg('commitment_signed', signature=commitsig_to_recv(), htlc_signature=htlc_sigs_to_recv(), ignore=ExpectMsg.ignore_all_gossip) ], # We received commitment_signed: [ Msg('channel_reestablish', channel_id=channel_id(), next_commitment_number=2, next_revocation_number=1, your_last_per_commitment_secret= remote_per_commitment_secret(0), my_current_per_commitment_point=local_keyset. per_commit_point(1)) ]) ]) ] runner.run(test)
def test_gossip(runner: Runner) -> None: # Make up a channel between nodes 02 and 03, using bitcoin privkeys 10 and 20 funding, funding_tx = Funding.from_utxo(*utxo(0), local_node_privkey='02', local_funding_privkey='10', remote_node_privkey='03', remote_funding_privkey='20') test = [Block(blockheight=102, txs=[tx_spendable]), Connect(connprivkey='03'), ExpectMsg('init'), Msg('init', globalfeatures='', features=''), Block(blockheight=103, number=6, txs=[funding_tx]), RawMsg(funding.channel_announcement('103x1x0', '')), # New peer connects, asking for initial_routing_sync. We *won't* relay channel_announcement, as there is no channel_update. Connect(connprivkey='05'), ExpectMsg('init'), Msg('init', globalfeatures='', features='08'), MustNotMsg('channel_announcement'), Disconnect(), RawMsg(funding.channel_update('103x1x0', Side.local, disable=False, cltv_expiry_delta=144, htlc_minimum_msat=0, fee_base_msat=1000, fee_proportional_millionths=10, timestamp=int(time.time()), htlc_maximum_msat=None), connprivkey='03'), # Now we'll relay to a new peer. Connect(connprivkey='05'), ExpectMsg('init'), Msg('init', globalfeatures='', features='08'), ExpectMsg('channel_announcement', short_channel_id='103x1x0'), ExpectMsg('channel_update', short_channel_id='103x1x0', message_flags=0, channel_flags=0), Disconnect(), # BOLT #7: # A node: # - SHOULD monitor the funding transactions in the blockchain, to # identify channels that are being closed. # - if the funding output of a channel is being spent: # - SHOULD be removed from the local network view AND be # considered closed. Block(blockheight=109, txs=[funding.close_tx(200, '99')]), Connect(connprivkey='05'), ExpectMsg('init'), Msg('init', globalfeatures='', features='08'), MustNotMsg('channel_announcement'), MustNotMsg('channel_update')] runner.run(test)
def test_open_accepter_channel(runner: Runner) -> None: # Needs modified spec, so don't even try unless we have that! if not 'open_channel2' in pyln.spec.bolt2.__dict__: return 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) input_index = 0 test = [ Block(blockheight=102, txs=[tx_spendable]), Connect(connprivkey='02'), ExpectMsg('init'), # BOLT-ff9a3470f5a0f475dc0909bf153620a73ca7b21e #9: # | 222/223 | `option_dual_fund` | Use v2 channel open Msg('init', globalfeatures='', features=bitfield(12, 223)), # Accepter side: we initiate a new channel. Msg( 'open_channel2', chain_hash=regtest_hash, funding_satoshis=funding_amount_for_utxo(input_index), dust_limit_satoshis=546, max_htlc_value_in_flight_msat=4294967295, htlc_minimum_msat=0, feerate_per_kw=253, feerate_per_kw_funding=253, # We use 5, because c-lightning runner uses 6, so this is different. to_self_delay=5, max_accepted_htlcs=483, locktime=0, podle_h2='00' * 32, 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), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('accept_channel2', channel_id=channel_id_v2(local_keyset), funding_satoshis=0, funding_pubkey=remote_funding_pubkey(), revocation_basepoint=remote_revocation_basepoint(), payment_basepoint=remote_payment_basepoint(), delayed_payment_basepoint=remote_delayed_payment_basepoint(), htlc_basepoint=remote_htlc_basepoint(), first_per_commitment_point=remote_per_commitment_point(0)), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), # Create and stash Funding object and FundingTx CreateFunding(*utxo(input_index), local_node_privkey='02', local_funding_privkey=local_funding_privkey, remote_node_privkey=runner.get_node_privkey(), remote_funding_privkey=remote_funding_privkey()), Commit(funding=funding(), opener=Side.local, local_keyset=local_keyset, local_to_self_delay=rcvd('accept_channel2.to_self_delay', int), remote_to_self_delay=sent('open_channel2.to_self_delay', int), local_amount=msat(sent('open_channel2.funding_satoshis', int)), remote_amount=0, local_dust_limit=546, remote_dust_limit=546, feerate=253, local_features=sent('init.features'), remote_features=rcvd('init.features')), Msg('tx_add_input', channel_id=rcvd('accept_channel2.channel_id'), serial_id=2, prevtx=tx_spendable, prevtx_vout=tx_out_for_index(input_index), max_witness_len=135, sequence=0xfffffffd, script=''), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('tx_complete', channel_id=rcvd('accept_channel2.channel_id')), Msg('tx_add_output', channel_id=rcvd('accept_channel2.channel_id'), serial_id=2, sats=funding_amount_for_utxo(input_index), script=locking_script()), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('tx_complete', channel_id=rcvd('accept_channel2.channel_id')), Msg('tx_complete', channel_id=rcvd('accept_channel2.channel_id')), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), Msg('commitment_signed', channel_id=rcvd('accept_channel2.channel_id'), signature=commitsig_to_send(), htlc_signature='[]'), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('commitment_signed', channel_id=rcvd('accept_channel2.channel_id'), signature=commitsig_to_recv()), ExpectMsg('tx_signatures', channel_id=rcvd('accept_channel2.channel_id'), txid=funding_txid(), witness_stack='[]'), # Mine the block! Block(blockheight=103, number=3, txs=[funding_tx()]), Msg('funding_locked', channel_id=rcvd('accept_channel2.channel_id'), next_per_commitment_point= '027eed8389cf8eb715d73111b73d94d2c2d04bf96dc43dfd5b0970d80b3617009d' ), ExpectMsg( 'funding_locked', channel_id=rcvd('accept_channel2.channel_id'), next_per_commitment_point= '032405cbd0f41225d5f203fe4adac8401321a9e05767c5f8af97d51d2e81fbb206' ), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))) ] runner.run(test)
def test_open_dual_accepter_channel(runner: Runner) -> None: # Needs modified spec, so don't even try unless we have that! if not 'open_channel2' in pyln.spec.bolt2.__dict__: return 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) input_index = 5 # Since technically these can be sent in any order, # we must specify this as ok! expected_add_input = ExpectMsg( 'tx_add_input', channel_id=rcvd('accept_channel2.channel_id'), max_witness_len=106, sequence=0xfffffffd, script='', if_match=odd_serial) expected_add_output = ExpectMsg( 'tx_add_output', channel_id=rcvd('accept_channel2.channel_id'), if_match=odd_serial) test = [ Block(blockheight=102, txs=[tx_spendable]), Connect(connprivkey='02'), ExpectMsg('init'), # BOLT-ff9a3470f5a0f475dc0909bf153620a73ca7b21e #9: # | 222/223 | `option_dual_fund` | Use v2 channel open Msg('init', globalfeatures='', features=bitfield(12, 223)), DualFundAccept(), # Accepter side: we initiate a new channel. Msg( 'open_channel2', chain_hash=regtest_hash, funding_satoshis=funding_amount_for_utxo(input_index), dust_limit_satoshis=546, max_htlc_value_in_flight_msat=4294967295, htlc_minimum_msat=0, feerate_per_kw=253, feerate_per_kw_funding=253, # We use 5, because c-lightning runner uses 6, so this is different. to_self_delay=5, max_accepted_htlcs=483, locktime=100, podle_h2='00' * 32, 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), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('accept_channel2', channel_id=channel_id_v2(local_keyset), funding_satoshis=funding_amount_for_utxo(input_index), funding_pubkey=remote_funding_pubkey(), revocation_basepoint=remote_revocation_basepoint(), payment_basepoint=remote_payment_basepoint(), delayed_payment_basepoint=remote_delayed_payment_basepoint(), htlc_basepoint=remote_htlc_basepoint(), first_per_commitment_point=remote_per_commitment_point(0)), # Create and stash Funding object and FundingTx CreateDualFunding(*utxo(input_index), funding_sats=agreed_funding(), locktime=sent('open_channel2.locktime', int), local_node_privkey='02', local_funding_privkey=local_funding_privkey, remote_node_privkey=runner.get_node_privkey(), remote_funding_privkey=remote_funding_privkey()), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), Msg('tx_add_input', channel_id=rcvd('accept_channel2.channel_id'), serial_id=0, sequence=0xfffffffd, prevtx=tx_spendable, prevtx_vout=tx_out_for_index(input_index), max_witness_len=135, script=''), AddInput(funding=funding(), privkey=privkey_for_index(input_index), serial_id=sent('tx_add_input.serial_id', int), prevtx=sent(), prevtx_vout=sent('tx_add_input.prevtx_vout', int), max_witness_len=sent('tx_add_input.max_witness_len', int), script=sent()), OneOf([ expected_add_input, Msg('tx_add_output', channel_id=rcvd('accept_channel2.channel_id'), serial_id=0, sats=agreed_funding(), script=funding_lockscript(local_funding_privkey)), expected_add_output ], [ expected_add_output, Msg('tx_add_output', channel_id=rcvd('accept_channel2.channel_id'), serial_id=2, sats=agreed_funding(), script=funding_lockscript(local_funding_privkey)), expected_add_input ]), AddInput(funding=funding(), serial_id=rcvd('tx_add_input.serial_id', int), prevtx=rcvd('tx_add_input.prevtx'), prevtx_vout=rcvd('tx_add_input.prevtx_vout', int), max_witness_len=rcvd('tx_add_input.max_witness_len', int), script=rcvd('tx_add_input.script')), AddOutput(funding=funding(), serial_id=rcvd('tx_add_output.serial_id', int), sats=rcvd('tx_add_output.sats', int), script=rcvd('tx_add_output.script')), AddOutput(funding=funding(), serial_id=sent('tx_add_output.serial_id', int), sats=sent('tx_add_output.sats', int), script=sent('tx_add_output.script')), FinalizeFunding(funding=funding()), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), Msg('tx_complete', channel_id=rcvd('accept_channel2.channel_id')), ExpectMsg('tx_complete', channel_id=rcvd('accept_channel2.channel_id')), Commit(funding=funding(), opener=Side.local, local_keyset=local_keyset, local_to_self_delay=rcvd('accept_channel2.to_self_delay', int), remote_to_self_delay=sent('open_channel2.to_self_delay', int), local_amount=msat(sent('open_channel2.funding_satoshis', int)), remote_amount=msat(rcvd('accept_channel2.funding_satoshis', int)), local_dust_limit=546, remote_dust_limit=546, feerate=253, local_features=sent('init.features'), remote_features=rcvd('init.features')), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), Msg('commitment_signed', channel_id=rcvd('accept_channel2.channel_id'), signature=commitsig_to_send(), htlc_signature='[]'), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))), ExpectMsg('commitment_signed', channel_id=rcvd('accept_channel2.channel_id'), signature=commitsig_to_recv()), ExpectMsg('tx_signatures', channel_id=rcvd('accept_channel2.channel_id'), txid=funding_txid()), AddWitnesses(funding=funding(), witness_stack=rcvd('witness_stack')), # Mine the block! Block(blockheight=103, number=3, txs=[funding_tx()]), Msg('funding_locked', channel_id=rcvd('accept_channel2.channel_id'), next_per_commitment_point=local_keyset.per_commit_point(1)), ExpectMsg('funding_locked', channel_id=rcvd('accept_channel2.channel_id'), next_per_commitment_point=remote_per_commitment_point(1)), # Ignore unknown odd messages TryAll([], RawMsg(bytes.fromhex('270F'))) ] runner.run(test)
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)