Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion lightningd/dual_open_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ hook_extract_psbt(const tal_t *ctx, struct subd *dualopend, const char *buffer,
}

*out = json_to_psbt(ctx, buffer, psbt_tok);
if (!*out)
/* We only deal in v2 */
if (!*out || !psbt_set_version(*out, 2))
fatal("Plugin must return a valid 'psbt' to a 'continue'd"
"%s %.*s", hook_name,
toks[0].end - toks[0].start,
Expand Down Expand Up @@ -2759,6 +2760,10 @@ json_openchannel_bump(struct command *cmd,
NULL))
return command_param_failed();

/* We only deal in v2 */
if (!psbt_set_version(info->psbt, 2))
return command_fail(cmd, LIGHTNINGD, "Could not set PSBT version.");

psbt_val = AMOUNT_SAT(0);
for (size_t i = 0; i < info->psbt->num_inputs; i++) {
struct amount_sat in_amt = psbt_input_get_amount(info->psbt, i);
Expand Down Expand Up @@ -2931,6 +2936,10 @@ json_openchannel_signed(struct command *cmd,
NULL))
return command_param_failed();

/* We only deal in v2 */
if (!psbt_set_version(psbt, 2))
return command_fail(cmd, LIGHTNINGD, "Could not set PSBT version.");

channel = channel_by_cid(cmd->ld, cid);
if (!channel)
return command_fail(cmd, FUNDING_UNKNOWN_CHANNEL,
Expand Down Expand Up @@ -3189,6 +3198,10 @@ static struct command_result *json_openchannel_update(struct command *cmd,
NULL))
return command_param_failed();

/* We only deal in v2 */
if (!psbt_set_version(psbt, 2))
return command_fail(cmd, LIGHTNINGD, "Could not set PSBT version.");

channel = channel_by_cid(cmd->ld, cid);
if (!channel)
return command_fail(cmd, FUNDING_UNKNOWN_CHANNEL,
Expand Down
32 changes: 32 additions & 0 deletions tests/plugins/df_accept_psbt_v0.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""Accepts a dual-funded open with an input of our own, returning version 0
PSBTs from the hooks, as an external wallet may."""

from pyln.client import Plugin

plugin = Plugin()


@plugin.hook('openchannel2')
def on_openchannel2(openchannel2, plugin, **kwargs):
psbt = plugin.rpc.call('fundpsbt', {
'satoshi': 100000,
'feerate': '{}perkw'.format(openchannel2['funding_feerate_per_kw']),
'startweight': 0,
'min_witness_weight': 110,
'excess_as_change': True,
'locktime': openchannel2['locktime'],
'nonwrapped': True})['psbt']
return {'result': 'continue',
'psbt': plugin.rpc.setpsbtversion(psbt, 0)['psbt'],
'our_funding_msat': 100000 * 1000}


@plugin.hook('openchannel2_sign')
def on_openchannel2_sign(openchannel2_sign, plugin, **kwargs):
signed = plugin.rpc.signpsbt(openchannel2_sign['psbt'])['signed_psbt']
return {'result': 'continue',
'psbt': plugin.rpc.setpsbtversion(signed, 0)['psbt']}


plugin.run()
65 changes: 65 additions & 0 deletions tests/test_opening.py
Original file line number Diff line number Diff line change
Expand Up @@ -4403,3 +4403,68 @@ def test_duplicate_channel_id_in_db(node_factory, bitcoind):
# And we can still start with both in the db.
l2.restart()
assert [c['channel_id'] for c in l2.rpc.listclosedchannels()['closedchannels']] == [cid, cid]


@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
@pytest.mark.openchannel('v2')
def test_v2_open_psbt_version_0(node_factory, bitcoind):
"""openchannel_update and openchannel_signed, and the accepter's hooks,
take version 0 PSBTs, as openchannel_init already does."""
plugin = os.path.join(os.getcwd(), 'tests/plugins/df_accept_psbt_v0.py')
l1, l2 = node_factory.get_nodes(2, opts=[{},
{'plugin': plugin,
'disable-plugin': 'funder'}])
l1.fundwallet(2000000)
l2.fundwallet(2000000)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)

def v0(psbt):
return l1.rpc.setpsbtversion(psbt, 0)['psbt']

psbt = l1.rpc.fundpsbt(500000, '7500perkw', 250, excess_as_change=True)['psbt']
start = l1.rpc.openchannel_init(l2.info['id'], 500000, v0(psbt))
chan_id = start['channel_id']
update = l1.rpc.openchannel_update(chan_id, v0(start['psbt']))
while not update['commitments_secured']:
update = l1.rpc.openchannel_update(chan_id, v0(update['psbt']))
l1.rpc.openchannel_signed(chan_id, v0(l1.rpc.signpsbt(update['psbt'])['signed_psbt']))
l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN')

bitcoind.generate_block(6, wait_for_mempool=1)
for n in (l1, l2):
n.daemon.wait_for_log(' to CHANNELD_NORMAL')
funding = only_one(l1.rpc.listpeerchannels()['channels'])['funding']
assert funding['remote_funds_msat'] == Millisatoshi(100000 * 1000)


@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
@pytest.mark.openchannel('v2')
def test_v2_rbf_psbt_version_0(node_factory, bitcoind):
"""openchannel_bump takes a version 0 PSBT."""
l1, l2 = node_factory.get_nodes(2)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
chan_amount = 100000
l1.fundwallet(2**24)

def v0(psbt):
return l1.rpc.setpsbtversion(psbt, 0)['psbt']

res = l1.rpc.fundchannel(l2.info['id'], chan_amount)
chan_id = res['channel_id']
vin = only_one(bitcoind.rpc.decoderawtransaction(res['tx'])['vin'])
l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN')

startweight = 42 + 172 # base weight, funding output
initpsbt = l1.rpc.utxopsbt(chan_amount, find_next_feerate(l1, l2), startweight,
["{}:{}".format(vin['txid'], vin['vout'])],
reservedok=True, excess_as_change=True)['psbt']
bump = l1.rpc.openchannel_bump(chan_id, chan_amount, v0(initpsbt))
update = l1.rpc.openchannel_update(chan_id, v0(bump['psbt']))
assert update['commitments_secured']
l1.rpc.openchannel_signed(chan_id, v0(l1.rpc.signpsbt(update['psbt'])['signed_psbt']))

bitcoind.generate_block(6, wait_for_mempool=1)
for n in (l1, l2):
n.daemon.wait_for_log(' to CHANNELD_NORMAL')
# The bump is what confirmed.
assert only_one(l1.rpc.listpeerchannels()['channels'])['funding_txid'] != res['txid']
Loading