2023-07-27 14:37:52 -07:00
|
|
|
from fixtures import * # noqa: F401,F403
|
2023-09-14 14:29:20 -04:00
|
|
|
from pyln.client import RpcError
|
2023-07-27 14:37:52 -07:00
|
|
|
import pytest
|
|
|
|
|
import unittest
|
2023-08-15 22:58:53 -04:00
|
|
|
import time
|
2023-09-19 17:35:57 -04:00
|
|
|
from utils import (
|
2024-01-31 13:46:20 +10:30
|
|
|
sync_blockheight, wait_for, TEST_NETWORK, first_scid, only_one
|
2023-09-19 17:35:57 -04:00
|
|
|
)
|
2023-07-27 14:37:52 -07:00
|
|
|
|
|
|
|
|
|
2023-08-08 09:42:50 +09:30
|
|
|
@pytest.mark.openchannel('v1')
|
2023-07-27 14:37:52 -07:00
|
|
|
@pytest.mark.openchannel('v2')
|
2023-08-08 09:42:50 +09:30
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
2023-07-27 14:37:52 -07:00
|
|
|
def test_splice(node_factory, bitcoind):
|
2023-08-08 09:42:50 +09:30
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
2023-07-27 14:37:52 -07:00
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-07-27 14:37:52 -07:00
|
|
|
result = l1.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
2023-07-27 14:37:52 -07:00
|
|
|
mempool = bitcoind.rpc.getrawmempool(True)
|
|
|
|
|
assert result['txid'] in list(mempool.keys())
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
2023-08-15 22:58:53 -04:00
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
2023-09-19 17:35:57 -04:00
|
|
|
|
|
|
|
|
|
2025-06-18 16:24:40 -04:00
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_two_chan_splice_in(node_factory, bitcoind):
|
|
|
|
|
l1, l2, l3 = node_factory.line_graph(3, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
# l2 will splice funds into the channels with l1 and l3 at the same time
|
|
|
|
|
|
|
|
|
|
chan_id1 = l2.get_channel_id(l1)
|
|
|
|
|
chan_id2 = l2.get_channel_id(l3)
|
|
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l2.rpc.fundpsbt("209000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
# Intiate splices to both channels
|
|
|
|
|
result = l2.rpc.splice_init(chan_id1, 100000, funds_result['psbt'])
|
|
|
|
|
result = l2.rpc.splice_init(chan_id2, 100000, result['psbt']) # start with psbt from first channel
|
|
|
|
|
|
|
|
|
|
done1 = False
|
|
|
|
|
done2 = False
|
|
|
|
|
sigs1 = False
|
|
|
|
|
sigs2 = False
|
|
|
|
|
|
|
|
|
|
while not done1 or not done2:
|
|
|
|
|
if not done1:
|
|
|
|
|
result = l2.rpc.splice_update(chan_id1, result['psbt'])
|
|
|
|
|
done1 = result['commitments_secured']
|
|
|
|
|
sigs1 = result['signatures_secured']
|
|
|
|
|
print("chan 1 " + result['psbt'])
|
|
|
|
|
if not done2:
|
|
|
|
|
result = l2.rpc.splice_update(chan_id2, result['psbt'])
|
|
|
|
|
done2 = result['commitments_secured']
|
|
|
|
|
sigs2 = result['signatures_secured']
|
|
|
|
|
print("chan 2 " + result['psbt'])
|
|
|
|
|
|
|
|
|
|
# Due to splice signing order, we may or may not have signatures
|
|
|
|
|
# from all peers, but we must have them from one.
|
|
|
|
|
print("Sigs1 " + str(sigs1) + ", Sigs2 " + str(sigs2))
|
|
|
|
|
assert(sigs1 or sigs2)
|
|
|
|
|
|
|
|
|
|
# Sign the inputs provided by `fundpsbt`
|
|
|
|
|
result = l2.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result['psbt'] = result['signed_psbt']
|
|
|
|
|
|
|
|
|
|
if sigs2:
|
|
|
|
|
# If chan2 gave us sigs, start with chan1
|
|
|
|
|
result = l2.rpc.splice_signed(chan_id1, result['psbt'])
|
|
|
|
|
result = l2.rpc.splice_signed(chan_id2, result['psbt'])
|
|
|
|
|
else:
|
|
|
|
|
# If chan1 gave us sigs, start with chan2
|
|
|
|
|
result = l2.rpc.splice_signed(chan_id2, result['psbt'])
|
|
|
|
|
result = l2.rpc.splice_signed(chan_id1, result['psbt'])
|
|
|
|
|
|
|
|
|
|
l3.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
|
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
|
|
|
|
assert result['txid'] in list(bitcoind.rpc.getrawmempool(True).keys())
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l3.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '1', 'no_1')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
inv = l3.rpc.invoice(10**2, '2', 'no_2')
|
|
|
|
|
l2.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
|
2025-05-05 15:57:54 -04:00
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_splice_rbf(node_factory, bitcoind):
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
funds_result = l1.rpc.addpsbtoutput(100000)
|
|
|
|
|
|
|
|
|
|
# Pay with fee by subjtracting 5000 from channel balance
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, -105000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
2025-05-05 15:57:54 -04:00
|
|
|
mempool = bitcoind.rpc.getrawmempool(True)
|
|
|
|
|
assert result['txid'] in list(mempool.keys())
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '1', 'no_1')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
funds_result = l1.rpc.addpsbtoutput(100000)
|
|
|
|
|
|
|
|
|
|
# Pay with fee by subjtracting 5000 from channel balance
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, -110000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '2', 'no_2')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
2025-07-09 11:30:23 +09:30
|
|
|
# Make sure l1 doesn't unilateral close if HTLC hasn't completely settled before deadline.
|
|
|
|
|
wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == [])
|
|
|
|
|
|
2025-05-05 15:57:54 -04:00
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
|
|
|
|
|
|
|
|
|
|
2025-02-06 18:34:15 -05:00
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_splice_nosign(node_factory, bitcoind):
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
|
|
|
|
try:
|
|
|
|
|
l1.rpc.splice_signed(chan_id, result['psbt'])
|
|
|
|
|
assert(False)
|
|
|
|
|
except RpcError as e:
|
|
|
|
|
assert(e.error['code'] == 358)
|
|
|
|
|
assert(e.error['message'] == "The PSBT is missing a signature. Have you signed it with `signpsbt`?")
|
|
|
|
|
|
|
|
|
|
|
2023-09-19 17:35:57 -04:00
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_splice_gossip(node_factory, bitcoind):
|
|
|
|
|
l1, l2, l3 = node_factory.line_graph(3, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
2023-10-02 09:29:48 +10:30
|
|
|
pre_splice_scid = first_scid(l1, l2)
|
2023-09-19 17:35:57 -04:00
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-09-19 17:35:57 -04:00
|
|
|
result = l1.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
2023-10-02 09:29:48 +10:30
|
|
|
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
|
2023-09-19 17:35:57 -04:00
|
|
|
|
pytest: fix flake in splice gossip test.
We can in fact see the new channel before this line is called:
```
2025-03-15T12:31:04.1472196Z @pytest.mark.openchannel('v1')
2025-03-15T12:31:04.1472616Z @pytest.mark.openchannel('v2')
2025-03-15T12:31:04.1473317Z @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
2025-03-15T12:31:04.1474271Z def test_splice_gossip(node_factory, bitcoind):
2025-03-15T12:31:04.1475078Z l1, l2, l3 = node_factory.line_graph(3, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
2025-03-15T12:31:04.1475781Z
2025-03-15T12:31:04.1476052Z chan_id = l1.get_channel_id(l2)
2025-03-15T12:31:04.1476460Z pre_splice_scid = first_scid(l1, l2)
2025-03-15T12:31:04.1476844Z
2025-03-15T12:31:04.1477134Z # add extra sats to pay fee
2025-03-15T12:31:04.1477741Z funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
2025-03-15T12:31:04.1478345Z
2025-03-15T12:31:04.1478765Z result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
2025-03-15T12:31:04.1479432Z result = l1.rpc.splice_update(chan_id, result['psbt'])
2025-03-15T12:31:04.1479994Z assert(result['commitments_secured'] is False)
2025-03-15T12:31:04.1480584Z result = l1.rpc.splice_update(chan_id, result['psbt'])
2025-03-15T12:31:04.1481089Z assert(result['commitments_secured'] is True)
2025-03-15T12:31:04.1481386Z result = l1.rpc.signpsbt(result['psbt'])
2025-03-15T12:31:04.1481860Z result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
2025-03-15T12:31:04.1482403Z
2025-03-15T12:31:04.1485960Z wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
2025-03-15T12:31:04.1489978Z wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
2025-03-15T12:31:04.1490796Z
2025-03-15T12:31:04.1491223Z bitcoind.generate_block(6, wait_for_mempool=result['txid'])
2025-03-15T12:31:04.1491767Z
2025-03-15T12:31:04.1492213Z # l3 will see channel dying, but still consider it OK for 12 blocks.
2025-03-15T12:31:04.1493174Z l3.daemon.wait_for_log(f'gossipd: channel {pre_splice_scid} closing soon due to the funding outpoint being spent')
2025-03-15T12:31:04.1494422Z assert len(l3.rpc.listchannels(short_channel_id=pre_splice_scid)['channels']) == 2
2025-03-15T12:31:04.1495293Z > assert len(l3.rpc.listchannels(source=l1.info['id'])['channels']) == 1
2025-03-15T12:31:04.1495937Z E AssertionError: assert 2 == 1
2025-03-15T12:31:04.1503185Z E + where 2 = len([{'active': True, 'amount_msat': 1000000000, 'base_fee_millisatoshi': 1, 'channel_flags': 1, ...}, {'active': True, 'amount_msat': 1100000000, 'base_fee_millisatoshi': 1, 'channel_flags': 1, ...}])
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-03-17 09:15:32 +10:30
|
|
|
bitcoind.generate_block(5, wait_for_mempool=result['txid'])
|
2023-09-19 17:35:57 -04:00
|
|
|
|
2023-10-02 09:29:48 +10:30
|
|
|
# l3 will see channel dying, but still consider it OK for 12 blocks.
|
|
|
|
|
l3.daemon.wait_for_log(f'gossipd: channel {pre_splice_scid} closing soon due to the funding outpoint being spent')
|
|
|
|
|
assert len(l3.rpc.listchannels(short_channel_id=pre_splice_scid)['channels']) == 2
|
|
|
|
|
assert len(l3.rpc.listchannels(source=l1.info['id'])['channels']) == 1
|
2023-09-19 17:35:57 -04:00
|
|
|
|
pytest: fix flake in splice gossip test.
We can in fact see the new channel before this line is called:
```
2025-03-15T12:31:04.1472196Z @pytest.mark.openchannel('v1')
2025-03-15T12:31:04.1472616Z @pytest.mark.openchannel('v2')
2025-03-15T12:31:04.1473317Z @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
2025-03-15T12:31:04.1474271Z def test_splice_gossip(node_factory, bitcoind):
2025-03-15T12:31:04.1475078Z l1, l2, l3 = node_factory.line_graph(3, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
2025-03-15T12:31:04.1475781Z
2025-03-15T12:31:04.1476052Z chan_id = l1.get_channel_id(l2)
2025-03-15T12:31:04.1476460Z pre_splice_scid = first_scid(l1, l2)
2025-03-15T12:31:04.1476844Z
2025-03-15T12:31:04.1477134Z # add extra sats to pay fee
2025-03-15T12:31:04.1477741Z funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
2025-03-15T12:31:04.1478345Z
2025-03-15T12:31:04.1478765Z result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
2025-03-15T12:31:04.1479432Z result = l1.rpc.splice_update(chan_id, result['psbt'])
2025-03-15T12:31:04.1479994Z assert(result['commitments_secured'] is False)
2025-03-15T12:31:04.1480584Z result = l1.rpc.splice_update(chan_id, result['psbt'])
2025-03-15T12:31:04.1481089Z assert(result['commitments_secured'] is True)
2025-03-15T12:31:04.1481386Z result = l1.rpc.signpsbt(result['psbt'])
2025-03-15T12:31:04.1481860Z result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
2025-03-15T12:31:04.1482403Z
2025-03-15T12:31:04.1485960Z wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
2025-03-15T12:31:04.1489978Z wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
2025-03-15T12:31:04.1490796Z
2025-03-15T12:31:04.1491223Z bitcoind.generate_block(6, wait_for_mempool=result['txid'])
2025-03-15T12:31:04.1491767Z
2025-03-15T12:31:04.1492213Z # l3 will see channel dying, but still consider it OK for 12 blocks.
2025-03-15T12:31:04.1493174Z l3.daemon.wait_for_log(f'gossipd: channel {pre_splice_scid} closing soon due to the funding outpoint being spent')
2025-03-15T12:31:04.1494422Z assert len(l3.rpc.listchannels(short_channel_id=pre_splice_scid)['channels']) == 2
2025-03-15T12:31:04.1495293Z > assert len(l3.rpc.listchannels(source=l1.info['id'])['channels']) == 1
2025-03-15T12:31:04.1495937Z E AssertionError: assert 2 == 1
2025-03-15T12:31:04.1503185Z E + where 2 = len([{'active': True, 'amount_msat': 1000000000, 'base_fee_millisatoshi': 1, 'channel_flags': 1, ...}, {'active': True, 'amount_msat': 1100000000, 'base_fee_millisatoshi': 1, 'channel_flags': 1, ...}])
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-03-17 09:15:32 +10:30
|
|
|
# Final one will allow splice announcement to proceed.
|
|
|
|
|
bitcoind.generate_block(1)
|
2023-10-02 09:29:48 +10:30
|
|
|
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL')
|
|
|
|
|
wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL')
|
2023-09-19 17:35:57 -04:00
|
|
|
|
2023-10-02 09:29:48 +10:30
|
|
|
post_splice_scid = first_scid(l1, l2)
|
|
|
|
|
assert post_splice_scid != pre_splice_scid
|
|
|
|
|
|
|
|
|
|
# l3 should see the new channel now.
|
2024-01-30 04:50:12 +10:30
|
|
|
wait_for(lambda: len(l3.rpc.listchannels(short_channel_id=post_splice_scid)['channels']) == 2)
|
2023-10-02 09:29:48 +10:30
|
|
|
assert len(l3.rpc.listchannels(short_channel_id=pre_splice_scid)['channels']) == 2
|
2023-09-19 17:35:57 -04:00
|
|
|
|
|
|
|
|
bitcoind.generate_block(7)
|
|
|
|
|
|
|
|
|
|
# The old channel should fall off l3's perspective
|
2023-10-02 09:29:48 +10:30
|
|
|
wait_for(lambda: l3.rpc.listchannels(short_channel_id=pre_splice_scid)['channels'] == [])
|
|
|
|
|
assert len(l3.rpc.listchannels(short_channel_id=post_splice_scid)['channels']) == 2
|
2023-09-19 17:35:57 -04:00
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
|
|
|
|
|
2023-10-02 09:29:48 +10:30
|
|
|
# Still looks normal from both sides
|
|
|
|
|
assert only_one(l1.rpc.listpeerchannels()['channels'])['short_channel_id'] == post_splice_scid
|
|
|
|
|
assert only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL'
|
|
|
|
|
assert only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['short_channel_id'] == post_splice_scid
|
|
|
|
|
assert only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL'
|
|
|
|
|
|
2023-09-19 17:35:57 -04:00
|
|
|
# Check for channel announcement failure
|
|
|
|
|
assert not l1.daemon.is_in_log("invalid local_channel_announcement")
|
|
|
|
|
assert not l2.daemon.is_in_log("invalid local_channel_announcement")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_splice_listnodes(node_factory, bitcoind):
|
|
|
|
|
# Here we do a splice but underfund it purposefully
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-09-19 17:35:57 -04:00
|
|
|
result = l1.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
|
|
|
|
assert len(l1.rpc.listnodes()['nodes']) == 2
|
|
|
|
|
assert len(l2.rpc.listnodes()['nodes']) == 2
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(7)
|
|
|
|
|
|
2023-12-13 16:05:43 +10:30
|
|
|
wait_for(lambda: len(l1.rpc.listnodes()['nodes']) == 2)
|
|
|
|
|
wait_for(lambda: len(l2.rpc.listnodes()['nodes']) == 2)
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_splice_out(node_factory, bitcoind):
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
funds_result = l1.rpc.addpsbtoutput(100000)
|
|
|
|
|
|
|
|
|
|
# Pay with fee by subjtracting 5000 from channel balance
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, -105000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-09-14 14:29:20 -04:00
|
|
|
result = l1.rpc.splice_signed(chan_id, result['psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
2023-09-14 14:29:20 -04:00
|
|
|
mempool = bitcoind.rpc.getrawmempool(True)
|
|
|
|
|
assert result['txid'] in list(mempool.keys())
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_invalid_splice(node_factory, bitcoind):
|
|
|
|
|
# Here we do a splice but underfund it purposefully
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None,
|
|
|
|
|
'may_reconnect': True,
|
|
|
|
|
'allow_warning': True})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
# We claim to add 100000 but in fact add nothing
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(RpcError) as rpc_error:
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
assert rpc_error.value.error["code"] == 357
|
|
|
|
|
assert rpc_error.value.error["message"] == "You provided 1000000000msat but committed to 1100000000msat."
|
|
|
|
|
|
|
|
|
|
# The splicing inflight should not have been left pending in the DB
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0
|
|
|
|
|
|
2024-01-22 17:43:05 -05:00
|
|
|
l1.daemon.wait_for_log(r'Restarting channeld after tx_abort on CHANNELD_NORMAL channel')
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0
|
|
|
|
|
|
|
|
|
|
# Now we do a real splice to confirm everything works after restart
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-09-14 14:29:20 -04:00
|
|
|
result = l1.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
2023-09-14 14:29:20 -04:00
|
|
|
mempool = bitcoind.rpc.getrawmempool(True)
|
|
|
|
|
assert result['txid'] in list(mempool.keys())
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_commit_crash_splice(node_factory, bitcoind):
|
|
|
|
|
# Here we do a normal splice out but force a restart after commiting.
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None,
|
|
|
|
|
'may_reconnect': True})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, -105000, l1.rpc.addpsbtoutput(100000)['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
l1.daemon.wait_for_log(r"Splice initiator: we commit")
|
|
|
|
|
|
|
|
|
|
l1.restart()
|
|
|
|
|
|
|
|
|
|
# The splicing inflight should have been left pending in the DB
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 1
|
|
|
|
|
|
2025-05-02 18:48:42 -04:00
|
|
|
l1.daemon.wait_for_log(r'peer_out WIRE_CHANNEL_REESTABLISH')
|
|
|
|
|
l1.daemon.wait_for_log(r'Got reestablish commit=1 revoke=0 inflights: 1, active splices: 1')
|
|
|
|
|
l1.daemon.wait_for_log(r'Splice resume check with local_next_funding: sent, remote_next_funding: received, inflights: 1')
|
|
|
|
|
l1.daemon.wait_for_log(r'Splice negotation, will not send commit, not recv commit, send signature, recv signature as initiator')
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 1
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
time.sleep(5)
|
2023-09-14 14:29:20 -04:00
|
|
|
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0
|
|
|
|
|
|
|
|
|
|
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
|
|
|
|
l1.rpc.pay(inv['bolt11'])
|
|
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
2023-10-05 16:50:43 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
|
|
|
|
def test_splice_stuck_htlc(node_factory, bitcoind, executor):
|
|
|
|
|
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
l3.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True)
|
|
|
|
|
|
|
|
|
|
inv = l3.rpc.invoice(10000000, '1', 'no_1')
|
|
|
|
|
executor.submit(l1.rpc.pay, inv['bolt11'])
|
|
|
|
|
l3.daemon.wait_for_log('their htlc 0 dev_ignore_htlcs')
|
|
|
|
|
|
|
|
|
|
# Now we should have a stuck invoice between l1 -> l2
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
2024-11-11 11:42:51 +10:30
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
2023-10-05 16:50:43 -04:00
|
|
|
result = l1.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
2025-05-12 17:27:00 -04:00
|
|
|
wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
|
2023-10-05 16:50:43 -04:00
|
|
|
mempool = bitcoind.rpc.getrawmempool(True)
|
|
|
|
|
assert result['txid'] in list(mempool.keys())
|
|
|
|
|
|
2024-01-31 13:46:20 +10:30
|
|
|
bitcoind.generate_block(1, wait_for_mempool=1)
|
|
|
|
|
# Don't have l2, l3 reject channel_announcement as too far in future.
|
|
|
|
|
sync_blockheight(bitcoind, [l1, l2, l3])
|
|
|
|
|
bitcoind.generate_block(5)
|
2023-10-05 16:50:43 -04:00
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
# Check that the splice doesn't generate a unilateral close transaction
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
2025-08-14 04:13:17 +09:30
|
|
|
|
|
|
|
|
|
2025-08-14 12:40:11 +09:30
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
2025-08-14 04:13:17 +09:30
|
|
|
def test_route_by_old_scid(node_factory, bitcoind):
|
2026-01-07 09:46:11 +10:30
|
|
|
opts = {'experimental-splicing': None, 'may_reconnect': True}
|
|
|
|
|
# l1 sometimes talks about pre-splice channels. l2 (being part of the splice) immediately forgets
|
|
|
|
|
# the old scid and uses the new one, then complains when l1 talks about it. Which is fine, but
|
|
|
|
|
# breaks CI.
|
|
|
|
|
l1opts = opts.copy()
|
|
|
|
|
l1opts['allow_warning'] = True
|
|
|
|
|
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, opts=[l1opts, opts, opts])
|
2025-08-14 04:13:17 +09:30
|
|
|
|
|
|
|
|
# Get pre-splice route.
|
|
|
|
|
inv = l3.rpc.invoice(10000000, 'test_route_by_old_scid', 'test_route_by_old_scid')
|
2025-08-14 12:40:12 +09:30
|
|
|
inv2 = l3.rpc.invoice(10000000, 'test_route_by_old_scid2', 'test_route_by_old_scid2')
|
pytest: fix flake in test_route_by_old_scid
```
2025-08-14T11:45:41.7353946Z # Now l1 tries to send using old scid: should work
2025-08-14T11:45:41.7354652Z l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2025-08-14T11:45:41.7355321Z > l1.rpc.waitsendpay(inv['payment_hash'])
2025-08-14T11:45:41.7355644Z
2025-08-14T11:45:41.7355791Z tests/test_splicing.py:528:
...
2025-08-14T11:45:41.7383073Z E pyln.client.lightning.RpcError: RPC call failed: method: waitsendpay, payload: {'payment_hash': '7b74fa9f6a889a16ebf89b8a9468302100f6ad50a771bbab2a16de58dcb1a9a4'}, error: {'code': 203, 'message': 'failed: WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS (reply from remote)', 'data': {'created_index': 1, 'id': 1, 'payment_hash': '7b74fa9f6a889a16ebf89b8a9468302100f6ad50a771bbab2a16de58dcb1a9a4', 'groupid': 1, 'destination': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', 'amount_msat': 10000000, 'amount_sent_msat': 10000101, 'created_at': 1755171392, 'status': 'pending', 'erring_index': 2, 'failcode': 16399, 'failcodename': 'WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS', 'erring_node': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', 'erring_channel': '103x2x0', 'erring_direction': 0, 'raw_message': '400f000000000098968000000072'}}
```
Caused by:
```
2025-08-14T11:45:41.9275961Z lightningd-3 2025-08-14T11:36:32.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Expiry cltv too soon 118 < 114 + 5
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-08-15 15:20:32 +09:30
|
|
|
route = l1.rpc.getroute(l3.info['id'], 10000000, 1, cltv=16)['route']
|
2025-08-14 04:13:17 +09:30
|
|
|
|
|
|
|
|
# Do a splice
|
|
|
|
|
funds_result = l2.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
chan_id = l2.get_channel_id(l3)
|
|
|
|
|
result = l2.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l2.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l2.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
|
|
|
|
result = l2.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l2.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
|
|
|
|
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
# Now l1 tries to send using old scid: should work
|
|
|
|
|
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
|
|
|
|
|
l1.rpc.waitsendpay(inv['payment_hash'])
|
2025-08-14 12:40:12 +09:30
|
|
|
|
|
|
|
|
# Let's splice again, so the original scid is two behind the times.
|
|
|
|
|
l3.fundwallet(200000)
|
|
|
|
|
funds_result = l3.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
chan_id = l3.get_channel_id(l2)
|
|
|
|
|
result = l3.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l3.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l3.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
|
|
|
|
result = l3.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l3.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
|
|
|
|
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
bitcoind.generate_block(6, wait_for_mempool=1)
|
|
|
|
|
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL')
|
|
|
|
|
|
|
|
|
|
# Now restart l2, make sure it remembers the original!
|
|
|
|
|
l2.restart()
|
pytest: fix reconnect flake in test_route_by_old_scid
We restart l2, then try to connect to l1. But l1 will be trying to
reconnect (and it can, since it was initially given l2's address), so
it can race us and we end up disconnecting because of simultaneous
connect:
```
2026-01-13T04:06:10.7490260Z > l2.rpc.connect(l1.info['id'], 'localhost', l1.port)
2026-01-13T04:06:10.7490667Z
2026-01-13T04:06:10.7490828Z tests/test_splicing.py:554:
...
2026-01-13T04:06:10.7525780Z > raise RpcError(method, payload, resp['error'])
2026-01-13T04:06:10.7527464Z E pyln.client.lightning.RpcError: RPC call failed: method: connect, payload: {'id': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', 'host': 'localhost', 'port': 46813}, error: {'code': 402, 'message': 'disconnected during connection'}
2026-01-13T04:06:10.7528832Z
...
2026-01-13T04:06:10.9411680Z lightningd-2 2026-01-13T03:59:59.463Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Connect OUT
2026-01-13T04:06:10.9412178Z lightningd-2 2026-01-13T03:59:59.463Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_out WIRE_INIT
2026-01-13T04:06:10.9412551Z lightningd-2 2026-01-13T03:59:59.463Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Connect IN
2026-01-13T04:06:10.9412947Z lightningd-2 2026-01-13T03:59:59.464Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_out WIRE_INIT
2026-01-13T04:06:10.9413342Z lightningd-2 2026-01-13T03:59:59.464Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_in WIRE_INIT
2026-01-13T04:06:10.9413624Z lightningd-2 2026-01-13T03:59:59.487Z TRACE lightningd: Calling peer_connected hook of plugin chanbackup
2026-01-13T04:06:10.9414057Z lightningd-2 2026-01-13T03:59:59.534Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Handed peer, entering loop
2026-01-13T04:06:10.9414451Z lightningd-2 2026-01-13T03:59:59.534Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_in WIRE_INIT
2026-01-13T04:06:10.9414851Z lightningd-2 2026-01-13T03:59:59.574Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-lightningd: peer reconnected
2026-01-13T04:06:10.9415250Z lightningd-2 2026-01-13T03:59:59.609Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-lightningd: peer_disconnected
2026-01-13T04:06:10.9415527Z lightningd-2 2026-01-13T03:59:59.685Z TRACE lightningd: Calling peer_connected hook of plugin chanbackup
2026-01-13T04:06:10.9415957Z lightningd-2 2026-01-13T03:59:59.687Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Handed peer, entering loop
2026-01-13T04:06:10.9416364Z lightningd-2 2026-01-13T03:59:59.757Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-lightningd: peer_disconnected
2026-01-13T04:06:10.9417067Z lightningd-2 2026-01-13T03:59:59.830Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Will try reconnect in 4 seconds
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2026-01-13 14:53:26 +10:30
|
|
|
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
2025-08-14 12:40:12 +09:30
|
|
|
l2.rpc.connect(l3.info['id'], 'localhost', l3.port)
|
|
|
|
|
|
|
|
|
|
wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is True)
|
|
|
|
|
l1.rpc.sendpay(route, inv2['payment_hash'], payment_secret=inv2['payment_secret'])
|
|
|
|
|
l1.rpc.waitsendpay(inv2['payment_hash'])
|
2025-10-23 10:08:10 +10:30
|
|
|
|
|
|
|
|
|
|
|
|
|
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
2025-10-23 10:08:43 +10:30
|
|
|
@pytest.mark.openchannel('v1')
|
|
|
|
|
@pytest.mark.openchannel('v2')
|
2025-10-23 10:08:10 +10:30
|
|
|
def test_splice_unannounced(node_factory, bitcoind):
|
|
|
|
|
l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=False, opts={'experimental-splicing': None})
|
|
|
|
|
|
|
|
|
|
chan_id = l1.get_channel_id(l2)
|
|
|
|
|
|
|
|
|
|
# add extra sats to pay fee
|
|
|
|
|
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
|
|
|
|
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is False)
|
|
|
|
|
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
|
|
|
|
assert(result['commitments_secured'] is True)
|
|
|
|
|
result = l1.rpc.signpsbt(result['psbt'])
|
|
|
|
|
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
|
|
|
|
|
|
|
|
|
bitcoind.generate_block(1, wait_for_mempool=1)
|
|
|
|
|
|
|
|
|
|
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
|
sync_blockheight(bitcoind, [l1, l2])
|